24 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
Anders Jensen
bfe0f9f49c Merge branch 'release/0.10.1' 2018-08-25 09:55:31 +02:00
Anders Jensen
9e38de34f2 updated readme and version 2018-08-25 09:55:11 +02:00
Anders Jensen
ec02a2e61d ensure priorities arent overwritten 2018-08-25 09:53:04 +02:00
Anders Jensen
0e63ed4ebc fixed small bug with file priority overwriting manual priority management 2018-08-25 09:16:07 +02:00
Anders Jensen
ba6c689d98 Merge tag '0.10.0' into develop
-
2018-08-24 21:23:40 +02:00
Anders Jensen
ce7f6efd6d Merge branch 'release/0.10.0' 2018-08-24 21:23:36 +02:00
Anders Jensen
7aed811b78 added reverse proxy mode 2018-08-24 21:20:53 +02:00
Anders Jensen
37753a23e4 Added support for waiting on end pieces 2018-08-24 18:02:52 +02:00
Anders Jensen
cdf2a5515a fixed small bug related to piece priority and torrent cleanup 2018-08-18 17:32:37 +02:00
Anders Jensen
14c23065b5 fixed compatibility issues and resume 2018-08-17 19:02:58 +02:00
Anders Jensen
3af5c420f8 initial move towards 1.0.0 2018-08-10 19:59:26 +02:00
Anders Jensen
cc13b032ea Merge tag '0.9.0' into develop
Bugfixes, Deluge 2
2018-02-20 17:57:23 +01:00
14 changed files with 949 additions and 534 deletions

15
.gitignore vendored
View File

@@ -23,8 +23,13 @@ dropin.cache
_trial_temp
*.komodoproject
docs/_build*
apiserver/metadata/imdbhandler.py
apiserver/metadata/malhandler.py
apiserver/services/search.py
apiserver/services/control.py
apiserver/services/files.py
.env*
# for bundling
thomas
six.py
rarfile.py
rfc6266.py
lepl
pytz

View File

@@ -38,12 +38,37 @@ 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.
## Todo
* [x] Add RAR streaming support
* [ ] Better feedback in interface about streams
* [ ] Better feedback when using API
* [x] Reverse proxy improvement (e.g. port different than bind port)
# 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.
## Version 0.10.0
* Rewrote large parts of the code
* Now using [thomas](https://github.com/JohnDoee/thomas) as file-reading core - this adds support for multi-rar streaming.
* Faster streaming by reading directly from disk
* Reverse proxy mode
## Version 0.9.0
* Few bugfixes
* Added support for Deluge 2

9
create-egg.sh Normal file
View File

@@ -0,0 +1,9 @@
virtualenv .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

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

@@ -37,12 +37,12 @@
# statement from all source files in the program, then also delete it here.
#
from setuptools import setup
from setuptools import setup, find_packages
__plugin_name__ = "Streaming"
__author__ = "Anders Jensen"
__author_email__ = "johndoee@tidalstream.org"
__version__ = "0.9.0"
__version__ = "0.10.3"
__url__ = "https://github.com/JohnDoee/deluge-streaming"
__license__ = "GPLv3"
__description__ = "Enables streaming of files while downloading them."
@@ -64,6 +64,18 @@ downloads ahead, this enables seeking in video files.
If you want to stream from a non-local computer, e.g. your seedbox, you will need to change the IP in option to the external server ip."""
__pkg_data__ = {__plugin_name__.lower(): ["template/*", "data/*"]}
REQUIREMENTS_PACKAGES = [
'thomas',
'lepl',
'pytz',
]
REQUIREMENTS_MODULES = [
'six',
'rarfile',
'rfc6266',
]
setup(
name=__plugin_name__,
version=__version__,
@@ -73,8 +85,10 @@ setup(
url=__url__,
license=__license__,
long_description=__long_description__ if __long_description__ else __description__,
# install_requires=REQUIREMENTS_PACKAGES,
packages=[__plugin_name__.lower()],
packages=[__plugin_name__.lower()] + ['%s.%s' % (x, y) for x in REQUIREMENTS_PACKAGES for y in find_packages(x)] + REQUIREMENTS_PACKAGES,
py_modules=REQUIREMENTS_MODULES,
package_data = __pkg_data__,
entry_points="""

File diff suppressed because it is too large Load Diff

View File

@@ -26,19 +26,26 @@
<property name="can_focus">False</property>
<property name="spacing">5</property>
<child>
<widget class="GtkCheckButton" id="input_download_only_streamed">
<property name="label" translatable="yes">Download only streamed files, skip the other files</property>
<widget class="GtkVBox" id="settings_vbox">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
<property name="can_focus">False</property>
<property name="spacing">5</property>
<child>
<widget class="GtkCheckButton" id="input_download_only_streamed">
<property name="label" translatable="yes">Download only streamed files, skip the other files</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
</widget>
</child>
@@ -160,9 +167,73 @@
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox54">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<widget class="GtkCheckButton" id="input_reverse_proxy_enabled">
<property name="label" translatable="yes">Enable Reverse Proxy</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox55">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<widget class="GtkLabel" id="reverse_proxy_base_url_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Reverse Proxy Base Url: </property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="input_reverse_proxy_base_url">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</widget>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<!-- <child>
<widget class="GtkRadioButton" id="input_serve_webui">
<property name="label" translatable="yes">Serve files via WebUI</property>
<property name="visible">True</property>
<property name="visible">False</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
@@ -175,16 +246,16 @@
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</child> -->
<child>
<widget class="GtkVBox" id="settings_vbox3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">5</property>
<child>
<!-- <child>
<widget class="GtkRadioButton" id="input_serve_standalone">
<property name="label" translatable="yes">Serve files via standalone</property>
<property name="visible">True</property>
<property name="visible">False</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
@@ -198,12 +269,12 @@
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</child> -->
<child>
<widget class="GtkAlignment" id="remote_alignment1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="left_padding">20</property>
<!-- <property name="left_padding">20</property> -->
<child>
<widget class="GtkVBox" id="remote_vbox1">
<property name="visible">True</property>
@@ -364,7 +435,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
<property name="position">5</property>
</packing>
</child>
</widget>
@@ -436,7 +507,7 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">5</property>
<child>
<!-- <child>
<widget class="GtkHBox" id="remote_username_hbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
@@ -476,7 +547,7 @@
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
</child> -->
<child>
<widget class="GtkHBox" id="remote_password_hbox1">
<property name="visible">True</property>
@@ -498,7 +569,7 @@
<widget class="GtkEntry" id="input_remote_password">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="visibility">False</property>
<property name="visibility">True</property>
<property name="invisible_char">•</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>

View File

@@ -35,7 +35,10 @@ PreferencePage = Ext.extend(Ext.Panel, {
title: 'Streaming',
border: false,
layout: 'form',
header: false,
autoScroll: true,
autoHeight: true,
width: 320,
_fields: {},
initComponent: function() {
@@ -50,10 +53,12 @@ PreferencePage = Ext.extend(Ext.Panel, {
title: 'Settings',
style: 'margin-bottom: 0px; padding-bottom: 0px; padding-top: 5px',
autoHeight: true,
labelWidth: 1,
labelAlign: 'top',
labelWidth: 150,
width: 300,
defaultType: 'textfield',
defaults: {
width: 180,
width: 280,
}
});
@@ -69,10 +74,12 @@ PreferencePage = Ext.extend(Ext.Panel, {
title: 'File Serving Settings',
style: 'margin-bottom: 0px; padding-bottom: 0px; padding-top: 5px',
autoHeight: true,
labelWidth: 110,
labelAlign: 'top',
labelWidth: 150,
width: 280,
defaultType: 'textfield',
defaults: {
width: 180,
width: 260,
}
});
@@ -89,13 +96,22 @@ PreferencePage = Ext.extend(Ext.Panel, {
maxValue: 99999,
}));
var field = fieldset.add({
xtype: 'togglefield',
name: 'reverse_proxy_base_url',
fieldLabel: 'Reverse Proxy Config',
});
om.bind('reverse_proxy_enabled', field.toggle);
om.bind('reverse_proxy_base_url', field.input);
fieldset = this.add({
xtype: 'fieldset',
border: false,
autoHeight: true,
defaultType: 'radio',
style: 'margin-bottom: 5px; margin-top: 0; padding-bottom: 5px; padding-top: 0;',
width: 240,
width: 280,
labelWidth: 1
});
@@ -130,7 +146,7 @@ PreferencePage = Ext.extend(Ext.Panel, {
autoHeight: true,
defaultType: 'radio',
style: 'margin-left: 24px; margin-bottom: 5px; margin-top: 0; padding-bottom: 5px; padding-top: 0;',
width: 240,
width: 280,
labelWidth: 1
});
@@ -164,12 +180,12 @@ PreferencePage = Ext.extend(Ext.Panel, {
om.bind('ssl_priv_key_path', fieldset.add({
name: 'ssl_priv_key_path',
fieldLabel: 'Private key file path'
fieldLabel: 'Private key file path',
}));
om.bind('ssl_cert_path', fieldset.add({
name: 'ssl_cert_path',
fieldLabel: 'Certificate and chains file path'
fieldLabel: 'Certificate and chains file path',
}));
fieldset = this.add({
@@ -178,10 +194,12 @@ PreferencePage = Ext.extend(Ext.Panel, {
title: 'Advanced settings',
style: 'margin-bottom: 0px; padding-bottom: 0px; padding-top: 5px',
autoHeight: true,
labelWidth: 1,
labelAlign: 'top',
labelWidth: 150,
width: 280,
defaultType: 'textfield',
defaults: {
width: 180,
width: 260,
}
});
@@ -189,7 +207,8 @@ PreferencePage = Ext.extend(Ext.Panel, {
xtype: 'checkbox',
name: 'allow_remote',
boxLabel: 'Allow remote control',
style: 'margin-left: 12px;'
style: 'margin-left: 12px;',
width: 150
}));
fieldset = this.add({
@@ -197,22 +216,23 @@ PreferencePage = Ext.extend(Ext.Panel, {
border: false,
style: 'margin-bottom: 0px; padding-bottom: 0px; padding-top: 5px',
autoHeight: true,
labelWidth: 110,
labelAlign: 'top',
labelWidth: 150,
width: 260,
defaultType: 'textfield',
defaults: {
width: 180,
width: 240,
}
});
om.bind('remote_username', fieldset.add({
xtype: 'textfield',
name: 'remote_username',
fieldLabel: 'Remote control username'
}));
// om.bind('remote_username', fieldset.add({
// xtype: 'textfield',
// name: 'remote_username',
// fieldLabel: 'Remote control username'
// }));
om.bind('remote_password', fieldset.add({
xtype: 'textfield',
inputType: 'password',
name: 'remote_password',
fieldLabel: 'Remote control password'
}));
@@ -233,7 +253,7 @@ PreferencePage = Ext.extend(Ext.Panel, {
labelWidth: 1,
defaultType: 'textfield',
defaults: {
width: 180,
width: 200,
}
});
@@ -308,7 +328,7 @@ PreferencePage = Ext.extend(Ext.Panel, {
var apiUrl = 'http';
if (optionsManager.get('use_ssl'))
apiUrl += 's';
apiUrl += '://' + optionsManager.get('ip') + ':' + optionsManager.get('port') + '/streaming/stream';
apiUrl += '://' + optionsManager.get('remote_username') + ':' + optionsManager.get('remote_password') + '@' + optionsManager.get('ip') + ':' + optionsManager.get('port') + '/streaming/stream';
Ext.getCmp('remote_url').setValue(apiUrl);
}
});

View File

@@ -124,10 +124,11 @@ class GtkUI(GtkPluginBase):
def on_apply_prefs(self):
log.debug("applying prefs for Streaming")
if self.glade.get_widget("input_serve_standalone").get_active():
serve_method = 'standalone'
elif self.glade.get_widget("input_serve_webui").get_active():
serve_method = 'webui'
serve_method = 'standalone'
# if self.glade.get_widget("input_serve_standalone").get_active():
# serve_method = 'standalone'
# elif self.glade.get_widget("input_serve_webui").get_active():
# serve_method = 'webui'
if self.glade.get_widget("input_ssl_cert_daemon").get_active():
ssl_source = 'daemon'
@@ -141,8 +142,11 @@ class GtkUI(GtkPluginBase):
"auto_open_stream_urls": self.glade.get_widget("input_auto_open_stream_urls").get_active(),
"allow_remote": self.glade.get_widget("input_allow_remote").get_active(),
"download_only_streamed": self.glade.get_widget("input_download_only_streamed").get_active(),
"reverse_proxy_enabled": self.glade.get_widget("input_reverse_proxy_enabled").get_active(),
# "download_in_order": self.glade.get_widget("input_download_in_order").get_active(),
"use_ssl": self.glade.get_widget("input_use_ssl").get_active(),
"remote_username": self.glade.get_widget("input_remote_username").get_text(),
# "remote_username": self.glade.get_widget("input_remote_username").get_text(),
"reverse_proxy_base_url": self.glade.get_widget("input_reverse_proxy_base_url").get_text(),
"remote_password": self.glade.get_widget("input_remote_password").get_text(),
"ssl_priv_key_path": self.glade.get_widget("input_ssl_priv_key_path").get_text(),
"ssl_cert_path": self.glade.get_widget("input_ssl_cert_path").get_text(),
@@ -173,18 +177,22 @@ class GtkUI(GtkPluginBase):
self.glade.get_widget("input_allow_remote").set_active(config["allow_remote"])
self.glade.get_widget("input_use_ssl").set_active(config["use_ssl"])
self.glade.get_widget("input_download_only_streamed").set_active(config["download_only_streamed"])
self.glade.get_widget("input_remote_username").set_text(config["remote_username"])
self.glade.get_widget("input_reverse_proxy_enabled").set_active(config["reverse_proxy_enabled"])
# self.glade.get_widget("input_download_in_order").set_active(config["download_in_order"])
# self.glade.get_widget("input_download_everything").set_active(not config["download_in_order"] and not config["download_only_streamed"])
# self.glade.get_widget("input_remote_username").set_text(config["remote_username"])
self.glade.get_widget("input_reverse_proxy_base_url").set_text(config["reverse_proxy_base_url"])
self.glade.get_widget("input_remote_password").set_text(config["remote_password"])
self.glade.get_widget("input_ssl_priv_key_path").set_text(config["ssl_priv_key_path"])
self.glade.get_widget("input_ssl_cert_path").set_text(config["ssl_cert_path"])
self.glade.get_widget("input_serve_standalone").set_active(config["serve_method"] == "standalone")
self.glade.get_widget("input_serve_webui").set_active(config["serve_method"] == "webui")
# self.glade.get_widget("input_serve_standalone").set_active(config["serve_method"] == "standalone")
# self.glade.get_widget("input_serve_webui").set_active(config["serve_method"] == "webui")
self.glade.get_widget("input_ssl_cert_daemon").set_active(config["ssl_source"] == "daemon")
self.glade.get_widget("input_ssl_cert_custom").set_active(config["ssl_source"] == "custom")
api_url = 'http%s://%s:%s/streaming/stream' % (('s' if config["use_ssl"] else ''), config["ip"], config["port"])
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.glade.get_widget("output_remote_url").set_text(api_url)
def stream_ready(self, result):

View File

@@ -29,8 +29,6 @@ class Resource(TwistedResource):
authenticated = True
if not authenticated:
print auth_header
print self.username, self.password
request.setResponseCode(401)
return 'Unauthorized'

68
streaming/torrentfile.py Normal file
View File

@@ -0,0 +1,68 @@
import logging
import mimetypes
import os
from thomas import InputBase
logger = logging.getLogger(__name__)
class DelugeTorrentInput(InputBase.find_plugin('file')):
plugin_name = 'torrent_file'
protocols = []
can_read_to = None
def __init__(self, item, torrent_handler, infohash, offset, path):
self.item = item
self.torrent_handler = torrent_handler
self.torrent = torrent_handler.get_torrent(infohash)
self.infohash = infohash
self.offset = offset
self.path = path
self.size, self.filename, self.content_type = self.get_info()
def get_info(self):
logger.info('Getting info about %r' % (self.path, ))
content_type = mimetypes.guess_type(self.path)[0] or 'bytes'
return self.item['size'], os.path.basename(self.path), content_type
def ensure_exists(self):
if not os.path.exists(self.path):
self.torrent.can_read(self.offset)
def seek(self, pos):
self.ensure_exists()
super(DelugeTorrentInput, self).seek(pos)
logger.debug('Seeking at %s torrentfile_id %r' % (self.tell(), id(self)))
self.torrent.add_reader(self, self.item.path, self.offset + self.tell(), self.offset + self.size)
def read(self, num):
self.ensure_exists()
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)))
tell = self.tell()
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))
if not self._open_file: # the file was closed while we waited
return b''
data = super(DelugeTorrentInput, self).read(real_num)
return data
def close(self):
self.torrent.remove_reader(self)
super(DelugeTorrentInput, self).close()