diff --git a/mopidy/http/__init__.py b/mopidy/http/__init__.py index 2e131817..95675386 100644 --- a/mopidy/http/__init__.py +++ b/mopidy/http/__init__.py @@ -35,7 +35,7 @@ class Extension(ext.Extension): def setup(self, registry): from .actor import HttpFrontend - from .handlers import mopidy_app_factory + from .handlers import make_mopidy_app_factory HttpFrontend.apps = registry['http:app'] HttpFrontend.statics = registry['http:static'] @@ -43,5 +43,6 @@ class Extension(ext.Extension): registry.add('frontend', HttpFrontend) registry.add('http:app', { 'name': 'mopidy', - 'factory': mopidy_app_factory, + 'factory': make_mopidy_app_factory( + registry['http:app'], registry['http:static']), }) diff --git a/mopidy/http/data/clients.html b/mopidy/http/data/clients.html new file mode 100644 index 00000000..feff4fee --- /dev/null +++ b/mopidy/http/data/clients.html @@ -0,0 +1,31 @@ + + + + + + Mopidy + + + +
+

Mopidy

+ +

This web server is a part of the Mopidy music server. To learn more + about Mopidy, please visit + www.mopidy.com.

+
+ +
+

Web clients

+ + + +

Web clients which are installed as Mopidy extensions will + automatically appear here.

+
+ + diff --git a/mopidy/http/handlers.py b/mopidy/http/handlers.py index 5c5d481f..00fbf083 100644 --- a/mopidy/http/handlers.py +++ b/mopidy/http/handlers.py @@ -15,19 +15,24 @@ from mopidy.utils import jsonrpc logger = logging.getLogger(__name__) -def mopidy_app_factory(config, core): - return [ - (r'/ws/?', WebSocketHandler, { - 'core': core, - }), - (r'/rpc', JsonRpcHandler, { - 'core': core, - }), - (r'/(.*)', StaticFileHandler, { - 'path': os.path.join(os.path.dirname(__file__), 'data'), - 'default_filename': 'index.html' - }), - ] +def make_mopidy_app_factory(apps, statics): + def mopidy_app_factory(config, core): + return [ + (r'/ws/?', WebSocketHandler, { + 'core': core, + }), + (r'/rpc', JsonRpcHandler, { + 'core': core, + }), + (r'/(.+)', StaticFileHandler, { + 'path': os.path.join(os.path.dirname(__file__), 'data'), + }), + (r'/', ClientListHandler, { + 'apps': apps, + 'statics': statics, + }), + ] + return mopidy_app_factory def make_jsonrpc_wrapper(core_actor): @@ -142,6 +147,24 @@ class JsonRpcHandler(tornado.web.RequestHandler): self.set_header('Content-Type', 'application/json; utf-8') +class ClientListHandler(tornado.web.RequestHandler): + def initialize(self, apps, statics): + self.apps = apps + self.statics = statics + + def get(self): + set_mopidy_headers(self) + + names = set() + for app in self.apps: + names.add(app['name']) + for static in self.statics: + names.add(static['name']) + names.discard('mopidy') + + self.render('data/clients.html', apps=sorted(list(names))) + + class StaticFileHandler(tornado.web.StaticFileHandler): def set_extra_headers(self, path): set_mopidy_headers(self) diff --git a/tests/http/test_server.py b/tests/http/test_server.py index f8e1b74e..a3ef4997 100644 --- a/tests/http/test_server.py +++ b/tests/http/test_server.py @@ -25,10 +25,13 @@ class HttpServerTest(tornado.testing.AsyncHTTPTestCase): core.get_version = mock.MagicMock(name='get_version') core.get_version.return_value = mopidy.__version__ + apps = [dict(name='testapp')] + statics = [dict(name='teststatic')] + http_frontend = actor.HttpFrontend(config=config, core=core) http_frontend.apps = [{ 'name': 'mopidy', - 'factory': handlers.mopidy_app_factory, + 'factory': handlers.make_mopidy_app_factory(apps, statics), }] return tornado.web.Application(http_frontend._get_request_handlers()) @@ -57,10 +60,12 @@ class RootAppTest(HttpServerTest): class MopidyAppTest(HttpServerTest): def test_should_return_index(self): response = self.fetch('/mopidy/', method='GET') + body = tornado.escape.to_unicode(response.body) self.assertIn( - 'This web server is a part of the Mopidy music server.', - tornado.escape.to_unicode(response.body)) + 'This web server is a part of the Mopidy music server.', body) + self.assertIn('testapp', body) + self.assertIn('teststatic', body) self.assertEqual( response.headers['X-Mopidy-Version'], mopidy.__version__) self.assertEqual(response.headers['Cache-Control'], 'no-cache')