http: Remove the REST API
This commit is contained in:
parent
532a915db8
commit
db5c671bd4
@ -20,10 +20,9 @@ Frontend which lets you control Mopidy through HTTP and WebSockets.
|
|||||||
When this frontend is included in :attr:`mopidy.settings.FRONTENDS`, it starts
|
When this frontend is included in :attr:`mopidy.settings.FRONTENDS`, it starts
|
||||||
a web server at the port specified by :attr:`mopidy.settings.HTTP_SERVER_PORT`.
|
a web server at the port specified by :attr:`mopidy.settings.HTTP_SERVER_PORT`.
|
||||||
|
|
||||||
This web server exposes both a REST web service at the URL ``/api``, and a
|
This web server exposes a WebSocket at ``/ws``. The WebSocket gives you access
|
||||||
WebSocket at ``/ws``. The REST API gives you access to most Mopidy
|
to Mopidy's full API and enables Mopidy to instantly push events to the client,
|
||||||
functionality, while the WebSocket enables Mopidy to instantly push events to
|
as they happen.
|
||||||
the client, as they happen.
|
|
||||||
|
|
||||||
The web server can also host any static files, for example the HTML, CSS,
|
The web server can also host any static files, for example the HTML, CSS,
|
||||||
JavaScript and images needed by a web based Mopidy client. To host static
|
JavaScript and images needed by a web based Mopidy client. To host static
|
||||||
|
|||||||
@ -16,7 +16,7 @@ try:
|
|||||||
except ImportError as import_error:
|
except ImportError as import_error:
|
||||||
raise exceptions.OptionalDependencyError(import_error)
|
raise exceptions.OptionalDependencyError(import_error)
|
||||||
|
|
||||||
from . import api, ws
|
from . import ws
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger('mopidy.frontends.http')
|
logger = logging.getLogger('mopidy.frontends.http')
|
||||||
@ -45,7 +45,6 @@ class HttpFrontend(pykka.ThreadingActor, CoreListener):
|
|||||||
|
|
||||||
def _create_app(self):
|
def _create_app(self):
|
||||||
root = RootResource()
|
root = RootResource()
|
||||||
root.api = api.ApiResource(self.core)
|
|
||||||
root.ws = ws.WebSocketResource(self.core)
|
root.ws = ws.WebSocketResource(self.core)
|
||||||
|
|
||||||
if settings.HTTP_SERVER_STATIC_DIR:
|
if settings.HTTP_SERVER_STATIC_DIR:
|
||||||
@ -60,9 +59,6 @@ class HttpFrontend(pykka.ThreadingActor, CoreListener):
|
|||||||
'tools.staticdir.index': 'index.html',
|
'tools.staticdir.index': 'index.html',
|
||||||
'tools.staticdir.dir': static_dir,
|
'tools.staticdir.dir': static_dir,
|
||||||
},
|
},
|
||||||
b'/api': {
|
|
||||||
'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
|
|
||||||
},
|
|
||||||
b'/ws': {
|
b'/ws': {
|
||||||
'tools.websocket.on': True,
|
'tools.websocket.on': True,
|
||||||
'tools.websocket.handler_cls': ws.WebSocketHandler,
|
'tools.websocket.handler_cls': ws.WebSocketHandler,
|
||||||
|
|||||||
@ -1,95 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from mopidy import exceptions
|
|
||||||
|
|
||||||
try:
|
|
||||||
import cherrypy
|
|
||||||
except ImportError as import_error:
|
|
||||||
raise exceptions.OptionalDependencyError(import_error)
|
|
||||||
|
|
||||||
|
|
||||||
class ApiResource(object):
|
|
||||||
exposed = True
|
|
||||||
|
|
||||||
def __init__(self, core):
|
|
||||||
self.core = core
|
|
||||||
self.player = PlayerResource(core)
|
|
||||||
self.tracklist = TrackListResource(core)
|
|
||||||
self.playlists = PlaylistsResource(core)
|
|
||||||
|
|
||||||
@cherrypy.tools.json_out()
|
|
||||||
def GET(self):
|
|
||||||
return {
|
|
||||||
'resources': {
|
|
||||||
'player': {
|
|
||||||
'href': '/api/player/',
|
|
||||||
},
|
|
||||||
'tracklist': {
|
|
||||||
'href': '/api/tracklist/',
|
|
||||||
},
|
|
||||||
'playlists': {
|
|
||||||
'href': '/api/playlists/',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PlayerResource(object):
|
|
||||||
exposed = True
|
|
||||||
|
|
||||||
def __init__(self, core):
|
|
||||||
self.core = core
|
|
||||||
|
|
||||||
@cherrypy.tools.json_out()
|
|
||||||
def GET(self):
|
|
||||||
properties = {
|
|
||||||
'state': self.core.playback.state,
|
|
||||||
'currentTrack': self.core.playback.current_track,
|
|
||||||
'consume': self.core.playback.consume,
|
|
||||||
'random': self.core.playback.random,
|
|
||||||
'repeat': self.core.playback.repeat,
|
|
||||||
'single': self.core.playback.single,
|
|
||||||
'volume': self.core.playback.volume,
|
|
||||||
'timePosition': self.core.playback.time_position,
|
|
||||||
}
|
|
||||||
for key, value in properties.items():
|
|
||||||
properties[key] = value.get()
|
|
||||||
if properties['currentTrack']:
|
|
||||||
properties['currentTrack'] = properties['currentTrack'].serialize()
|
|
||||||
return {'properties': properties}
|
|
||||||
|
|
||||||
|
|
||||||
class TrackListResource(object):
|
|
||||||
exposed = True
|
|
||||||
|
|
||||||
def __init__(self, core):
|
|
||||||
self.core = core
|
|
||||||
|
|
||||||
@cherrypy.tools.json_out()
|
|
||||||
def GET(self):
|
|
||||||
tl_tracks_future = self.core.tracklist.tl_tracks
|
|
||||||
current_tl_track_future = self.core.playback.current_tl_track
|
|
||||||
tracks = []
|
|
||||||
for tl_track in tl_tracks_future.get():
|
|
||||||
track = tl_track.track.serialize()
|
|
||||||
track['tlid'] = tl_track.tlid
|
|
||||||
tracks.append(track)
|
|
||||||
current_tl_track = current_tl_track_future.get()
|
|
||||||
return {
|
|
||||||
'currentTrackTlid': current_tl_track and current_tl_track.tlid,
|
|
||||||
'tracks': tracks,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PlaylistsResource(object):
|
|
||||||
exposed = True
|
|
||||||
|
|
||||||
def __init__(self, core):
|
|
||||||
self.core = core
|
|
||||||
|
|
||||||
@cherrypy.tools.json_out()
|
|
||||||
def GET(self):
|
|
||||||
playlists = self.core.playlists.playlists.get()
|
|
||||||
return {
|
|
||||||
'playlists': [p.serialize() for p in playlists],
|
|
||||||
}
|
|
||||||
@ -79,14 +79,6 @@
|
|||||||
you'll always have the following services available.</p>
|
you'll always have the following services available.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="box">
|
|
||||||
<h2>Web service</h2>
|
|
||||||
|
|
||||||
<p>Mopidy makes it's API available for use over HTTP at
|
|
||||||
<a href="/api/">/api/</a>. The service tries to be RESTful. It serves and
|
|
||||||
eats JSON data.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<h2>WebSocket endpoint</h2>
|
<h2>WebSocket endpoint</h2>
|
||||||
|
|
||||||
|
|||||||
@ -1,120 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import pykka
|
|
||||||
|
|
||||||
from tests import unittest
|
|
||||||
|
|
||||||
from mopidy import core
|
|
||||||
from mopidy.backends import dummy
|
|
||||||
from mopidy.frontends.http import api
|
|
||||||
from mopidy.models import Track
|
|
||||||
|
|
||||||
|
|
||||||
class ApiResourceTest(unittest.TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
self.backend = dummy.DummyBackend.start(audio=None).proxy()
|
|
||||||
self.core = core.Core.start(backends=[self.backend]).proxy()
|
|
||||||
self.api = api.ApiResource(core=self.core)
|
|
||||||
|
|
||||||
self.core.playlists.create('x')
|
|
||||||
self.core.playlists.create('y')
|
|
||||||
self.core.playlists.create('z')
|
|
||||||
self.core.tracklist.append([
|
|
||||||
Track(uri='dummy:a'),
|
|
||||||
Track(uri='dummy:b'),
|
|
||||||
Track(uri='dummy:c'),
|
|
||||||
])
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
pykka.ActorRegistry.stop_all()
|
|
||||||
|
|
||||||
def test_api_get_returns_list_of_resources(self):
|
|
||||||
result = self.api.GET()
|
|
||||||
|
|
||||||
self.assertIn('resources', result)
|
|
||||||
|
|
||||||
self.assertIn('player', result['resources'])
|
|
||||||
self.assertEquals(
|
|
||||||
'/api/player/', result['resources']['player']['href'])
|
|
||||||
|
|
||||||
self.assertIn('tracklist', result['resources'])
|
|
||||||
self.assertEquals(
|
|
||||||
'/api/tracklist/', result['resources']['tracklist']['href'])
|
|
||||||
|
|
||||||
self.assertIn('playlists', result['resources'])
|
|
||||||
self.assertEquals(
|
|
||||||
'/api/playlists/', result['resources']['playlists']['href'])
|
|
||||||
|
|
||||||
def test_player_get_returns_playback_properties(self):
|
|
||||||
result = self.api.player.GET()
|
|
||||||
|
|
||||||
self.assertIn('properties', result)
|
|
||||||
|
|
||||||
self.assertIn('state', result['properties'])
|
|
||||||
self.assertEqual('stopped', result['properties']['state'])
|
|
||||||
|
|
||||||
self.assertIn('currentTrack', result['properties'])
|
|
||||||
self.assertEqual(None, result['properties']['currentTrack'])
|
|
||||||
|
|
||||||
self.assertIn('consume', result['properties'])
|
|
||||||
self.assertEqual(False, result['properties']['consume'])
|
|
||||||
|
|
||||||
self.assertIn('random', result['properties'])
|
|
||||||
self.assertEqual(False, result['properties']['random'])
|
|
||||||
|
|
||||||
self.assertIn('repeat', result['properties'])
|
|
||||||
self.assertEqual(False, result['properties']['repeat'])
|
|
||||||
|
|
||||||
self.assertIn('single', result['properties'])
|
|
||||||
self.assertEqual(False, result['properties']['single'])
|
|
||||||
|
|
||||||
self.assertIn('volume', result['properties'])
|
|
||||||
self.assertEqual(None, result['properties']['volume'])
|
|
||||||
|
|
||||||
self.assertIn('timePosition', result['properties'])
|
|
||||||
self.assertEqual(0, result['properties']['timePosition'])
|
|
||||||
|
|
||||||
def test_player_state_changes_when_playing(self):
|
|
||||||
self.core.playback.play()
|
|
||||||
|
|
||||||
result = self.api.player.GET()
|
|
||||||
|
|
||||||
self.assertEqual('playing', result['properties']['state'])
|
|
||||||
|
|
||||||
def test_player_volume_changes(self):
|
|
||||||
self.core.playback.volume = 37
|
|
||||||
|
|
||||||
result = self.api.player.GET()
|
|
||||||
|
|
||||||
self.assertEqual(37, result['properties']['volume'])
|
|
||||||
|
|
||||||
def test_tracklist_returns_tracklist(self):
|
|
||||||
result = self.api.tracklist.GET()
|
|
||||||
|
|
||||||
self.assertIn('tracks', result)
|
|
||||||
self.assertEqual(3, len(result['tracks']))
|
|
||||||
|
|
||||||
self.assertEqual('dummy:a', result['tracks'][0]['uri'])
|
|
||||||
self.assertEqual(0, result['tracks'][0]['tlid'])
|
|
||||||
|
|
||||||
self.assertEqual('dummy:b', result['tracks'][1]['uri'])
|
|
||||||
self.assertEqual(1, result['tracks'][1]['tlid'])
|
|
||||||
|
|
||||||
self.assertEqual('dummy:c', result['tracks'][2]['uri'])
|
|
||||||
self.assertEqual(2, result['tracks'][2]['tlid'])
|
|
||||||
|
|
||||||
def test_tracklist_includes_current_track(self):
|
|
||||||
self.core.playback.play()
|
|
||||||
|
|
||||||
result = self.api.tracklist.GET()
|
|
||||||
|
|
||||||
self.assertIn('currentTrackTlid', result)
|
|
||||||
self.assertEqual(0, result['currentTrackTlid'])
|
|
||||||
|
|
||||||
def test_playlists_returns_playlists(self):
|
|
||||||
result = self.api.playlists.GET()
|
|
||||||
|
|
||||||
self.assertIn('playlists', result)
|
|
||||||
self.assertEqual('x', result['playlists'][0]['name'])
|
|
||||||
self.assertEqual('y', result['playlists'][1]['name'])
|
|
||||||
self.assertEqual('z', result['playlists'][2]['name'])
|
|
||||||
Loading…
Reference in New Issue
Block a user