Initial Configuration Push

This commit is contained in:
CCOSTAN
2016-10-11 16:42:06 +00:00
parent b83eeadfcb
commit 5127bc2109
2145 changed files with 298464 additions and 0 deletions

1
deps/roku/__init__.py vendored Normal file
View File

@@ -0,0 +1 @@
from roku.core import Roku, Application, RokuException, __version__

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

249
deps/roku/core.py vendored Normal file
View File

@@ -0,0 +1,249 @@
import logging
import requests
import xml.etree.ElementTree as ET
from six.moves.urllib_parse import urlparse
from roku import discovery
__version__ = '3.1.2'
roku_logger = logging.getLogger('roku')
COMMANDS = {
'home': 'Home',
'reverse': 'Rev',
'forward': 'Fwd',
'play': 'Play',
'select': 'Select',
'left': 'Left',
'right': 'Right',
'down': 'Down',
'up': 'Up',
'back': 'Back',
'replay': 'InstantReplay',
'info': 'Info',
'backspace': 'Backspace',
'search': 'Search',
'enter': 'Enter',
'literal': 'Lit',
'volume_down': 'VolumeDown',
'volume_mute': 'VolumeMute',
'volume_up': 'VolumeUp',
}
SENSORS = ('acceleration', 'magnetic', 'orientation', 'rotation')
TOUCH_OPS = ('up', 'down', 'press', 'move', 'cancel')
class RokuException(Exception):
pass
class Application(object):
def __init__(self, id, version, name, roku=None):
self.id = str(id)
self.version = version
self.name = name
self.roku = roku
def __repr__(self):
return ('<Application: [%s] %s v%s>' %
(self.id, self.name, self.version))
@property
def icon(self):
if self.roku:
return self.roku.icon(self)
def launch(self):
if self.roku:
self.roku.launch(self)
def store(self):
if self.roku:
self.roku.store(self)
class DeviceInfo(object):
def __init__(self, modelname, modelnum, swversion, sernum, userdevicename):
self.modelname = modelname
self.modelnum = modelnum
self.swversion = swversion
self.sernum = sernum
self.userdevicename = userdevicename
def __repr__(self):
return ('<DeviceInfo: %s-%s, SW v%s, Ser# %s, Name %s>' %
(self.modelname, self.modelnum, self.swversion, self.sernum, self.userdevicename))
class Roku(object):
@classmethod
def discover(self, *args, **kwargs):
rokus = []
for device in discovery.discover(*args, **kwargs):
o = urlparse(device.location)
rokus.append(Roku(o.hostname, o.port))
return rokus
def __init__(self, host, port=8060):
self.host = host
self.port = port
self._conn = None
def __repr__(self):
return "<Roku: %s:%s>" % (self.host, self.port)
def __getattr__(self, name):
if name not in COMMANDS and name not in SENSORS:
raise AttributeError('%s is not a valid method' % name)
def command(*args):
if name in SENSORS:
keys = ['%s.%s' % (name, axis) for axis in ('x', 'y', 'z')]
params = dict(zip(keys, args))
self.input(params)
elif name == 'literal':
for char in args[0]:
path = '/keypress/%s_%s' % (COMMANDS[name], char.upper())
self._post(path)
else:
path = '/keypress/%s' % COMMANDS[name]
self._post(path)
return command
def __getitem__(self, key):
key = str(key)
app = self._app_for_name(key)
if not app:
app = self._app_for_id(key)
return app
def _app_for_name(self, name):
for app in self.apps:
if app.name == name:
return app
def _app_for_id(self, app_id):
for app in self.apps:
if app.id == app_id:
return app
def _connect(self):
if self._conn is None:
self._conn = requests.Session()
def _get(self, path, *args, **kwargs):
return self._call('GET', path, *args, **kwargs)
def _post(self, path, *args, **kwargs):
return self._call('POST', path, *args, **kwargs)
def _call(self, method, path, *args, **kwargs):
self._connect()
roku_logger.debug(path)
url = 'http://%s:%s%s' % (self.host, self.port, path)
if method not in ('GET', 'POST'):
raise ValueError('only GET and POST HTTP methods are supported')
func = getattr(self._conn, method.lower())
resp = func(url, *args, **kwargs)
if resp.status_code != 200:
raise RokuException(resp.content)
return resp.content
@property
def apps(self):
applications = []
resp = self._get('/query/apps')
root = ET.fromstring(resp)
for app_node in root:
app = Application(
id=app_node.get('id'),
version=app_node.get('version'),
name=app_node.text,
roku=self,
)
applications.append(app)
return applications
@property
def device_info(self):
resp = self._get('/query/device-info')
root = ET.fromstring(resp)
dinfo = DeviceInfo(
modelname=root.find('model-name').text,
modelnum=root.find('model-number').text,
swversion=''.join([
root.find('software-version').text,
'.',
root.find('software-build').text
]),
sernum=root.find('serial-number').text,
userdevicename=root.find('user-device-name').text
)
return dinfo
@property
def commands(self):
return sorted(COMMANDS.keys())
def icon(self, app):
return self._get('/query/icon/%s' % app.id)
def launch(self, app):
if app.roku and app.roku != self:
raise RokuException('this app belongs to another Roku')
return self._post('/launch/%s' % app.id)
def store(self, app):
return self._post('/launch/11', params={'contentID': app.id})
def input(self, params):
return self._post('/input', params=params)
def touch(self, x, y, op='down'):
if op not in TOUCH_OPS:
raise RokuException('%s is not a valid touch operation' % op)
params = {
'touch.0.x': x,
'touch.0.y': y,
'touch.0.op': op,
}
self.input(params)
@property
def current_app(self):
resp = self._get('/query/active-app')
root = ET.fromstring(resp)
app_node = root.find('screensaver')
if app_node is None:
app_node = root.find('app')
if app_node is None:
return None
return Application(
id=app_node.get('id'),
version=app_node.get('version'),
name=app_node.text,
roku=self,
)

67
deps/roku/discovery.py vendored Normal file
View File

@@ -0,0 +1,67 @@
"""
Code adapted from Dan Krause.
https://gist.github.com/dankrause/6000248
http://github.com/dankrause
"""
import socket
import six
from six.moves import http_client
ST_DIAL = 'urn:dial-multiscreen-org:service:dial:1'
ST_ECP = 'roku:ecp'
class _FakeSocket(six.BytesIO):
def makefile(self, *args, **kw):
return self
class SSDPResponse(object):
def __init__(self, response):
self.location = response.getheader('location')
self.usn = response.getheader('usn')
self.st = response.getheader('st')
self.cache = response.getheader('cache-control').split('=')[1]
def __repr__(self):
return '<SSDPResponse({location}, {st}, {usn})'.format(**self.__dict__)
def discover(timeout=2, retries=1, st=ST_ECP):
group = ('239.255.255.250', 1900)
message = '\r\n'.join([
'M-SEARCH * HTTP/1.1',
'HOST: {0}:{1}'.format(*group),
'MAN: "ssdp:discover"',
'ST: {st}', 'MX: 3', '', ''])
socket.setdefaulttimeout(timeout)
responses = {}
for _ in range(retries):
sock = socket.socket(
socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
m = message.format(st=st)
if six.PY2:
sock.sendto(m, group)
elif six.PY3:
sock.sendto(m.encode(), group)
while 1:
try:
rhttp = http_client.HTTPResponse(_FakeSocket(sock.recv(1024)))
rhttp.begin()
if rhttp.status == 200:
rssdp = SSDPResponse(rhttp)
responses[rssdp.location] = rssdp
except socket.timeout:
break
return responses.values()

1
deps/roku/emulator/__init__.py vendored Normal file
View File

@@ -0,0 +1 @@
from roku.emulator.core import Emulator, DEFAULT_APPS

Binary file not shown.

Binary file not shown.

29
deps/roku/emulator/core.py vendored Normal file
View File

@@ -0,0 +1,29 @@
from roku import Application
DEFAULT_APPS = [
Application(1, '1.0', 'Hulu Plus'),
Application(2, '2.0', 'TWiT'),
Application(3, '3.0', 'Whisky Media'),
Application(4, '4.0', 'Netflix'),
]
class Emulator(object):
def __init__(self, apps=None):
self._apps = apps or DEFAULT_APPS
def __call__(self, command, *args, **kwargs):
pass
def add_app(self, app):
pass
def get_icon(self, app_id):
pass
def launch_app(self, app_id):
pass
def list_apps(self):
pass

7
deps/roku/proxy.py vendored Normal file
View File

@@ -0,0 +1,7 @@
from roku.core import Roku
class Proxy(object):
def __init__(self, remote_host, remote_port=8060, local_port=8060):
pass

28
deps/roku/server.py vendored Normal file
View File

@@ -0,0 +1,28 @@
from flask import Flask, request
app = Flask(__name__)
@app.route('/keypress/<key>')
def keypress(key):
pass
@app.route('/launch/<code>')
def launch(code):
app_id = request.args.get('contentID', None)
pass
@app.route('/query/apps')
def list_apps():
pass
@app.route('/query/icon/<app_id>')
def app_icon(app_id):
pass
if __name__ == '__main__':
app.run(port=8060, debug=True)

155
deps/roku/tests.py vendored Normal file
View File

@@ -0,0 +1,155 @@
import unittest
from .core import Roku, COMMANDS
TEST_APPS = (
('11', '1.0.1', 'Fake Roku Channel Store'),
('22', '2.0.2', 'Fake Netflix'),
('33', '3.0.3', 'Fake YouTube'),
)
TEST_DEV_INFO = ''.join([
'<?xml version="1.0" encoding="UTF-8" ?>',
'<device-info>',
' <udn>00000000-1111-2222-3333-444444444444</udn>',
' <serial-number>111111111111</serial-number>',
' <device-id>222222222222</device-id>',
' <vendor-name>Roku</vendor-name>',
' <model-number>4200X</model-number>',
' <model-name>Roku 3</model-name>',
' <wifi-mac>00:11:22:33:44:55</wifi-mac>',
' <ethernet-mac>00:11:22:33:44:56</ethernet-mac>',
' <network-type>ethernet</network-type>',
' <user-device-name/>',
' <software-version>7.00</software-version>',
' <software-build>09044</software-build>',
' <secure-device>true</secure-device>',
' <language>en</language>',
' <country>US</country>',
' <locale>en_US</locale>',
' <time-zone>US/Eastern</time-zone>',
' <time-zone-offset>-300</time-zone-offset>',
' <power-mode>PowerOn</power-mode>',
' <developer-enabled>false</developer-enabled>',
' <search-enabled>true</search-enabled>',
' <voice-search-enabled>true</voice-search-enabled>',
' <notifications-enabled>true</notifications-enabled>',
' <notifications-first-use>false</notifications-first-use>',
' <headphones-connected>false</headphones-connected>',
'</device-info>'
])
class TestRoku(Roku):
def __init__(self, *args, **kwargs):
super(TestRoku, self).__init__(*args, **kwargs)
self._calls = []
def _call(self, method, path, *args, **kwargs):
resp = None
if path == '/query/apps':
fmt = '<app id="%s" version="%s">%s</app>'
resp = '<apps>%s</apps>' % "".join(fmt % a for a in TEST_APPS)
elif path == '/query/device-info':
resp = TEST_DEV_INFO
self._calls.append((method, path, args, kwargs, resp))
return resp
def calls(self):
return self._calls
def last_call(self):
return self._calls[-1]
class RokuTestCase(unittest.TestCase):
def setUp(self):
self.roku = TestRoku('0.0.0.0')
def testApps(self):
apps = self.roku.apps
self.assertEqual(len(apps), 3)
for i, app in enumerate(apps):
self.assertEqual(app.id, TEST_APPS[i][0])
self.assertEqual(app.version, TEST_APPS[i][1])
self.assertEqual(app.name, TEST_APPS[i][2])
app = self.roku['22']
self.assertEqual(app.id, TEST_APPS[1][0])
self.assertEqual(app.version, TEST_APPS[1][1])
self.assertEqual(app.name, TEST_APPS[1][2])
app = self.roku['Fake YouTube']
self.assertEqual(app.id, TEST_APPS[2][0])
self.assertEqual(app.version, TEST_APPS[2][1])
self.assertEqual(app.name, TEST_APPS[2][2])
def testDeviceInfo(self):
d = self.roku.device_info
self.assertEqual(d.modelname, 'Roku 3')
self.assertEqual(d.modelnum, '4200X')
self.assertEqual(d.swversion, '7.00.09044')
self.assertEqual(d.sernum, '111111111111')
def testCommands(self):
for cmd in self.roku.commands:
if cmd == 'literal':
continue
getattr(self.roku, cmd)()
call = self.roku.last_call()
self.assertEqual(call[0], 'POST')
self.assertEqual(call[1], '/keypress/%s' % COMMANDS[cmd])
self.assertEqual(call[2], ())
self.assertEqual(call[3], {})
def testLiteral(self):
text = 'Stargate'
self.roku.literal(text)
for i, call in enumerate(self.roku.calls()):
self.assertEqual(call[0], 'POST')
self.assertEqual(call[1], '/keypress/Lit_%s' % text[i].upper())
self.assertEqual(call[2], ())
self.assertEqual(call[3], {})
def testStore(self):
for app in self.roku.apps:
self.roku.store(app)
call = self.roku.last_call()
self.assertEqual(call[0], 'POST')
self.assertEqual(call[1], '/launch/11')
self.assertEqual(call[2], ())
self.assertEqual(call[3], {'params': {'contentID': app.id}})
def testLaunch(self):
for app in self.roku.apps:
self.roku.launch(app)
call = self.roku.last_call()
self.assertEqual(call[0], 'POST')
self.assertEqual(call[1], '/launch/%s' % app.id)
self.assertEqual(call[2], ())
self.assertEqual(call[3], {'params': {'contentID': app.id}})
if __name__ == '__main__':
unittest.main()