diff --git a/mopidy/frontends/http/api.py b/mopidy/frontends/http/api.py index 0003f06a..66caeb1a 100644 --- a/mopidy/frontends/http/api.py +++ b/mopidy/frontends/http/api.py @@ -11,14 +11,96 @@ class ApiResource(object): 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): - playback_state = self.core.playback.state.get() - track = self.core.playback.current_track.get() - if track: - track = track.serialize() return { - 'playback_state': playback_state, - 'current_track': track, + '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): + futures = { + 'state': self.core.playback.state, + 'current_track': 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, + 'time_position': self.core.playback.time_position, + } + current_track = futures['current_track'].get() + if current_track: + current_track = current_track.serialize() + return { + 'properties': { + 'state': futures['state'].get(), + 'currentTrack': current_track, + 'consume': futures['consume'].get(), + 'random': futures['random'].get(), + 'repeat': futures['repeat'].get(), + 'single': futures['single'].get(), + 'volume': futures['volume'].get(), + 'timePosition': futures['time_position'].get(), + } + } + + +class TrackListResource(object): + exposed = True + + def __init__(self, core): + self.core = core + + @cherrypy.tools.json_out() + def GET(self): + futures = { + 'cp_tracks': self.core.current_playlist.cp_tracks, + 'current_cp_track': self.core.playback.current_cp_track, + } + cp_tracks = futures['cp_tracks'].get() + tracks = [] + for cp_track in cp_tracks: + track = cp_track.track.serialize() + track['cpid'] = cp_track.cpid + tracks.append(track) + current_cp_track = futures['current_cp_track'].get() + return { + 'currentTrackCpid': current_cp_track and current_cp_track.cpid, + 'tracks': tracks, + } + + +class PlaylistsResource(object): + exposed = True + + def __init__(self, core): + self.core = core + + @cherrypy.tools.json_out() + def GET(self): + playlists = self.core.stored_playlists.playlists.get() + return { + 'playlists': [p.serialize() for p in playlists], } diff --git a/tests/frontends/http/__init__.py b/tests/frontends/http/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/frontends/http/api_test.py b/tests/frontends/http/api_test.py new file mode 100644 index 00000000..14f94773 --- /dev/null +++ b/tests/frontends/http/api_test.py @@ -0,0 +1,118 @@ +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.stored_playlists.create('x') + self.core.stored_playlists.create('y') + self.core.stored_playlists.create('z') + self.core.current_playlist.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_current_playlist(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]['cpid']) + + self.assertEqual('dummy:b', result['tracks'][1]['uri']) + self.assertEqual(1, result['tracks'][1]['cpid']) + + self.assertEqual('dummy:c', result['tracks'][2]['uri']) + self.assertEqual(2, result['tracks'][2]['cpid']) + + def test_tracklist_includes_current_track(self): + self.core.playback.play() + + result = self.api.tracklist.GET() + + self.assertIn('currentTrackCpid', result) + self.assertEqual(0, result['currentTrackCpid']) + + def test_playlists_returns_stored_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']) diff --git a/tests/frontends/http/ws_test.py b/tests/frontends/http/ws_test.py new file mode 100644 index 00000000..0615052e --- /dev/null +++ b/tests/frontends/http/ws_test.py @@ -0,0 +1,5 @@ +from tests import unittest + + +class WebSocketsTest(unittest.TestCase): + pass # TODO