29 Commits

Author SHA1 Message Date
Anders Jensen
8ab94d22b9 Merge branch 'release/0.10.4' 2018-09-21 21:43:25 +02:00
Anders Jensen
4b1d3799b5 trying to fix bug with setting priority too often 2018-09-21 21:43:12 +02:00
Anders Jensen
1398e5042b Highlighting, added examples, fixed small title stuff 2018-09-17 21:50:23 +02:00
Anders Jensen
de54ed067a Updated readme to fix #20 2018-09-17 19:00:05 +02:00
Anders Jensen
7cfdfc79fe Merge tag '0.10.3' into develop
-
2018-09-11 18:39:00 +02:00
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 1039 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,91 @@ 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)
* [ ] Fix problems when removing torrent from Deluge (sea of errors)
# HTTP API Usage
## Prerequisite
Install and enable the plugin. Afterwards, head into Streaming settings and enable "Allow remote control".
The URL found in the "Remote control url" field is where the API can be reached. The auth used is Basic Auth.
## Usage
There is only one end-point and that is where a torrent stream can be requested.
Both return the same responses and all responses are JSON encoded.
All successfully authenticated responses have status code 200.
## POST /streaming/stream
POST body must be the raw torrent you want to stream. No form formatting or anything can be used.
List of URL GET Arguments
* **path**: Path inside the torrent file to either a folder or a file you want to stream. The plugin will try to guess the best one. **Optional**. **Default**: '' (i.e. find the best file in the whole torrent)
* **infohash**: Infohash of the torrent you want to stream, can make it a bit faster as it can avoid reading POST body. **Optional**.
* **label**: If label plugin is enabled and the torrent is actually added then give the torrent this label. **Optional**. **Default**: ''
* **wait_for_end_pieces**: Wait for the first and last piece in the streamed file to be fully downloaded. Can be necessary for some video players. It also enforces that the torrent can be actually downloaded. If the key exist with any (even empty) value, the feature is enabled. **Optional**. **Default**: false
## GET /streaming/stream
* **infohash**: Does the same as when POSTed. **Mandatory**.
* **path**: Does the same as when POSTed. **Optional**.
* **wait_for_end_pieces**: Does the same as when POSTed. **Optional**.
## Success Response
```json
{
"status": "success", # Always equals this
"filename" "horse.mkv", # Filename of the streamed torrent
"url": "http://example.com/" # URL where the file can be reached by e.g. a media player
}
```
## Error Response
```json
{
"status": "error", # Always equals this
"message" "Torrent failed" # description for why it failed
}
```
# Version Info
## Version 0.10.4
* Trying to set max priority less as it destroys performance
## Version 0.10.3
* 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.4"
__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()