diff --git a/mopidy/__main__.py b/mopidy/__main__.py index 57dd3538..d6342033 100644 --- a/mopidy/__main__.py +++ b/mopidy/__main__.py @@ -18,6 +18,7 @@ logger = logging.getLogger('mopidy.main') def main(): options = _parse_options() _setup_logging(options.verbosity_level, options.dump) + logger.info('-- Starting Mopidy --') get_or_create_folder('~/.mopidy/') core_queue = multiprocessing.Queue() get_class(settings.SERVER)(core_queue) diff --git a/mopidy/backends/gstreamer.py b/mopidy/backends/gstreamer.py index 826a3669..3d6a8fdd 100644 --- a/mopidy/backends/gstreamer.py +++ b/mopidy/backends/gstreamer.py @@ -38,10 +38,10 @@ class GStreamerBackend(BaseBackend): def __init__(self, *args, **kwargs): super(GStreamerBackend, self).__init__(*args, **kwargs) - self.playback = GStreamerPlaybackController(self) + self.library = GStreamerLibraryController(self) self.stored_playlists = GStreamerStoredPlaylistsController(self) self.current_playlist = BaseCurrentPlaylistController(self) - self.library = GStreamerLibraryController(self) + self.playback = GStreamerPlaybackController(self) self.uri_handlers = [u'file://'] @@ -120,16 +120,22 @@ class GStreamerPlaybackController(BasePlaybackController): class GStreamerStoredPlaylistsController(BaseStoredPlaylistsController): def __init__(self, *args): super(GStreamerStoredPlaylistsController, self).__init__(*args) - self._folder = os.path.expanduser(settings.PLAYLIST_FOLDER) + self._folder = os.path.expanduser(settings.LOCAL_PLAYLIST_FOLDER) self.refresh() def refresh(self): playlists = [] + logger.info('Loading playlists from %s', self._folder) + for m3u in glob.glob(os.path.join(self._folder, '*.m3u')): name = os.path.basename(m3u)[:len('.m3u')] - track_uris = parse_m3u(m3u) - tracks = map(lambda u: Track(uri=u), track_uris) + tracks = [] + for uri in parse_m3u(m3u): + try: + tracks.append(self.backend.library.lookup(uri)) + except LookupError, e: + logger.error('Playlist item could not be added: %s', e) playlist = Playlist(tracks=tracks, name=name) # FIXME playlist name needs better handling @@ -188,8 +194,11 @@ class GStreamerLibraryController(BaseLibraryController): self.refresh() def refresh(self, uri=None): - tracks = parse_mpd_tag_cache(settings.TAG_CACHE, - settings.MUSIC_FOLDER) + tracks = parse_mpd_tag_cache(settings.LOCAL_TAG_CACHE, + settings.LOCAL_MUSIC_FOLDER) + + logger.info('Loading songs in %s from %s', + settings.LOCAL_MUSIC_FOLDER, settings.LOCAL_TAG_CACHE) for track in tracks: self._uri_mapping[track.uri] = track @@ -198,7 +207,7 @@ class GStreamerLibraryController(BaseLibraryController): try: return self._uri_mapping[uri] except KeyError: - raise LookupError + raise LookupError('%s not found.' % uri) def find_exact(self, field, query): if not query: diff --git a/mopidy/mixers/alsa.py b/mopidy/mixers/alsa.py index 4fd84fd2..bbb99772 100644 --- a/mopidy/mixers/alsa.py +++ b/mopidy/mixers/alsa.py @@ -39,6 +39,7 @@ class AlsaMixer(BaseMixer): return [u'Master', u'PCM'] def _get_volume(self): + # FIXME does not seem to see external volume changes. return self._mixer.getvolume()[0] def _set_volume(self, volume): diff --git a/mopidy/mpd/frontend.py b/mopidy/mpd/frontend.py index e1942a7e..1fc48045 100644 --- a/mopidy/mpd/frontend.py +++ b/mopidy/mpd/frontend.py @@ -432,11 +432,14 @@ class MpdFrontend(object): argument is given, displays information only for the song ``SONGPOS`` or the range of songs ``START:END``. - *ncmpc:* + *ncmpc and mpc:* - uses negative indexes, like ``playlistinfo "-1"``, to request - information on the last track in the playlist. + the entire playlist """ + if songpos == "-1": + songpos = None + if songpos is not None: songpos = int(songpos) start = songpos @@ -1420,7 +1423,7 @@ class MpdFrontend(object): matches = self.backend.stored_playlists.search(name) if matches: self.backend.current_playlist.load(matches[0]) - self.backend.playback.new_playlist_loaded_callback() + self.backend.playback.new_playlist_loaded_callback() # FIXME not needed? @handle_pattern(r'^playlistadd "(?P[^"]+)" "(?P[^"]+)"$') def _stored_playlist_playlistadd(self, name, uri): diff --git a/mopidy/settings.py b/mopidy/settings.py index cc1b629e..0d501c10 100644 --- a/mopidy/settings.py +++ b/mopidy/settings.py @@ -119,13 +119,16 @@ SPOTIFY_LIB_APPKEY = u'~/.mopidy/spotify_appkey.key' SPOTIFY_LIB_CACHE = u'~/.mopidy/libspotify_cache' #: Path to playlist folder with m3u files. -PLAYLIST_FOLDER = u'~/.mopidy/playlists' +#: LOCAL_PLAYLIST_FOLDER = u'~/.mopidy/playlists' +LOCAL_PLAYLIST_FOLDER = u'~/.mopidy/playlists' #: Path to folder with local music. -MUSIC_FOLDER = u'~/music' +#: LOCAL_MUSIC_FOLDER = u'~/music' +LOCAL_MUSIC_FOLDER = u'~/music' -#: Path to MPD tag_cache for local music. -TAG_CACHE = u'~/.mopidy/tag_cache' +#: Path to MPD tag_cache for local music +#: LOCAL_TAG_CACHE = u'~/.mopidy/tag_cache' +LOCAL_TAG_CACHE = u'~/.mopidy/tag_cache' # Import user specific settings dotdir = os.path.expanduser(u'~/.mopidy/') diff --git a/mopidy/utils.py b/mopidy/utils.py index 8ddca4c8..758fe2e3 100644 --- a/mopidy/utils.py +++ b/mopidy/utils.py @@ -35,6 +35,7 @@ def get_or_create_folder(folder): def path_to_uri(*paths): path = os.path.join(*paths) + #path = os.path.expanduser(path) # FIXME path = path.encode('utf-8') if sys.platform == 'win32': return 'file:' + urllib.pathname2url(path) @@ -212,12 +213,13 @@ def _convert_mpd_data(data, tracks, music_dir): track_kwargs['name'] = data['title'] if data['file'][0] == '/': - path = os.path.join(music_dir, data['file'][1:]) + path = data['file'][1:] else: - path = os.path.join(music_dir, data['file']) + path = data['file'] - track_kwargs['uri'] = path_to_uri(path) + track_kwargs['uri'] = path_to_uri(music_dir, path) track_kwargs['length'] = int(data.get('time', 0)) * 1000 + track_kwargs['id'] = len(tracks) track = Track(**track_kwargs) tracks.add(track) diff --git a/tests/backends/base.py b/tests/backends/base.py index 2974b316..129ac4ff 100644 --- a/tests/backends/base.py +++ b/tests/backends/base.py @@ -960,16 +960,26 @@ class BaseStoredPlaylistsControllerTest(object): backend_class = None def setUp(self): - self.original_folder = settings.PLAYLIST_FOLDER - settings.PLAYLIST_FOLDER = tempfile.mkdtemp() + self.original_playlist_folder = settings.LOCAL_PLAYLIST_FOLDER + self.original_tag_cache = settings.LOCAL_TAG_CACHE + self.original_music_folder = settings.LOCAL_MUSIC_FOLDER + + settings.LOCAL_PLAYLIST_FOLDER = tempfile.mkdtemp() + settings.LOCAL_TAG_CACHE = data_folder('library_tag_cache') + settings.LOCAL_MUSIC_FOLDER = data_folder('') + self.backend = self.backend_class(mixer=DummyMixer()) self.stored = self.backend.stored_playlists def tearDown(self): self.backend.destroy() - if os.path.exists(settings.PLAYLIST_FOLDER): - shutil.rmtree(settings.PLAYLIST_FOLDER) - settings.PLAYLIST_FOLDER = self.original_folder + + if os.path.exists(settings.LOCAL_PLAYLIST_FOLDER): + shutil.rmtree(settings.LOCAL_PLAYLIST_FOLDER) + + settings.LOCAL_PLAYLIST_FOLDER = self.original_playlist_folder + settings.LOCAL_TAG_CACHE = self.original_tag_cache + settings.LOCAL_MUSIC_FOLDER = self.original_music_folder def test_create(self): playlist = self.stored.create('test') @@ -1064,6 +1074,9 @@ class BaseStoredPlaylistsControllerTest(object): self.stored.save(playlist) self.assert_(playlist in self.stored.playlists) + def test_playlist_with_unknown_track(self): + raise SkipTest + class BaseLibraryControllerTest(object): artists = [Artist(name='artist1'), Artist(name='artist2'), Artist()] @@ -1071,10 +1084,10 @@ class BaseLibraryControllerTest(object): Album(name='album2', artists=artists[1:2]), Album()] tracks = [Track(name='track1', length=4000, artists=artists[:1], - album=albums[0], uri='file://' + data_folder('uri1')), + album=albums[0], uri='file://' + data_folder('uri1'), id=0), Track(name='track2', length=4000, artists=artists[1:2], - album=albums[1], uri='file://' + data_folder('uri2')), - Track()] + album=albums[1], uri='file://' + data_folder('uri2'), id=1), + Track(id=3)] def setUp(self): self.backend = self.backend_class(mixer=DummyMixer()) diff --git a/tests/backends/gstreamer_test.py b/tests/backends/gstreamer_test.py index 7e682d36..c308d545 100644 --- a/tests/backends/gstreamer_test.py +++ b/tests/backends/gstreamer_test.py @@ -58,13 +58,13 @@ class GStreamerStoredPlaylistsControllerTest(BaseStoredPlaylistsControllerTest, backend_class = GStreamerBackend def test_created_playlist_is_persisted(self): - path = os.path.join(settings.PLAYLIST_FOLDER, 'test.m3u') + path = os.path.join(settings.LOCAL_PLAYLIST_FOLDER, 'test.m3u') self.assert_(not os.path.exists(path)) self.stored.create('test') self.assert_(os.path.exists(path)) def test_saved_playlist_is_persisted(self): - path = os.path.join(settings.PLAYLIST_FOLDER, 'test2.m3u') + path = os.path.join(settings.LOCAL_PLAYLIST_FOLDER, 'test2.m3u') self.assert_(not os.path.exists(path)) self.stored.save(Playlist(name='test2')) self.assert_(os.path.exists(path)) @@ -72,13 +72,13 @@ class GStreamerStoredPlaylistsControllerTest(BaseStoredPlaylistsControllerTest, def test_deleted_playlist_get_removed(self): playlist = self.stored.create('test') self.stored.delete(playlist) - path = os.path.join(settings.PLAYLIST_FOLDER, 'test.m3u') + path = os.path.join(settings.LOCAL_PLAYLIST_FOLDER, 'test.m3u') self.assert_(not os.path.exists(path)) def test_renamed_playlist_gets_moved(self): playlist = self.stored.create('test') - file1 = os.path.join(settings.PLAYLIST_FOLDER, 'test.m3u') - file2 = os.path.join(settings.PLAYLIST_FOLDER, 'test2.m3u') + file1 = os.path.join(settings.LOCAL_PLAYLIST_FOLDER, 'test.m3u') + file2 = os.path.join(settings.LOCAL_PLAYLIST_FOLDER, 'test2.m3u') self.assert_(not os.path.exists(file2)) self.stored.rename(playlist, 'test2') self.assert_(not os.path.exists(file1)) @@ -88,7 +88,7 @@ class GStreamerStoredPlaylistsControllerTest(BaseStoredPlaylistsControllerTest, track = Track(uri=generate_song(1)) uri = track.uri[len('file://'):] playlist = Playlist(tracks=[track], name='test') - path = os.path.join(settings.PLAYLIST_FOLDER, 'test.m3u') + path = os.path.join(settings.LOCAL_PLAYLIST_FOLDER, 'test.m3u') self.stored.save(playlist) @@ -98,7 +98,7 @@ class GStreamerStoredPlaylistsControllerTest(BaseStoredPlaylistsControllerTest, self.assertEqual(uri, contents.strip()) def test_playlists_are_loaded_at_startup(self): - track = Track(uri=generate_song(1)) + track = Track(uri=path_to_uri(data_folder('uri2'))) playlist = Playlist(tracks=[track], name='test') self.stored.save(playlist) @@ -130,15 +130,18 @@ class GStreamerLibraryControllerTest(BaseLibraryControllerTest, backend_class = GStreamerBackend def setUp(self): - self.original_tag_cache = settings.TAG_CACHE - self.original_music_folder = settings.MUSIC_FOLDER - settings.TAG_CACHE = data_folder('library_tag_cache') - settings.MUSIC_FOLDER = data_folder('') + self.original_tag_cache = settings.LOCAL_TAG_CACHE + self.original_music_folder = settings.LOCAL_MUSIC_FOLDER + + settings.LOCAL_TAG_CACHE = data_folder('library_tag_cache') + settings.LOCAL_MUSIC_FOLDER = data_folder('') + super(GStreamerLibraryControllerTest, self).setUp() def tearDown(self): - settings.TAG_CACHE = self.original_tag_cache - settings.MUSIC_FOLDER = self.original_music_folder + settings.LOCAL_TAG_CACHE = self.original_tag_cache + settings.LOCAL_MUSIC_FOLDER = self.original_music_folder + super(GStreamerLibraryControllerTest, self).tearDown() if __name__ == '__main__': diff --git a/tests/mpd/frontend_test.py b/tests/mpd/frontend_test.py index d62a5e08..edacccf1 100644 --- a/tests/mpd/frontend_test.py +++ b/tests/mpd/frontend_test.py @@ -523,6 +523,10 @@ class CurrentPlaylistHandlerTest(unittest.TestCase): self.assertEqual(self.b.current_playlist.playlist.tracks[5], needle) self.assert_(u'OK' in result) + def test_add_with_uri_not_found_in_library_should_ack(self): + result = self.h.handle_request(u'add "dummy://foo"') + self.assert_(u'ACK No such song' in result) + def test_addid_without_songpos(self): needle = Track(uri='dummy://foo', id=137) self.b.library._library = [Track(), Track(), needle, Track()] @@ -557,7 +561,6 @@ class CurrentPlaylistHandlerTest(unittest.TestCase): self.assert_(u'ACK Position out of bounds' in result) def test_addid_with_uri_not_found_in_library_should_ack(self): - self.b.library._library = [Track(), Track(), Track()] result = self.h.handle_request(u'addid "dummy://foo"') self.assert_(u'ACK No such song' in result) @@ -727,22 +730,27 @@ class CurrentPlaylistHandlerTest(unittest.TestCase): self.assert_(u'ACK "id=25" match no tracks' in result) def test_playlistinfo_without_songpos_or_range(self): + # FIXME testing just ok is not enough result = self.h.handle_request(u'playlistinfo') self.assert_(u'OK' in result) def test_playlistinfo_with_songpos(self): + # FIXME testing just ok is not enough result = self.h.handle_request(u'playlistinfo "5"') self.assert_(u'OK' in result) - def test_playlistinfo_with_negative_songpos(self): - result = self.h.handle_request(u'playlistinfo "-1"') - self.assert_(u'OK' in result) + def test_playlistinfo_with_negative_songpos_same_as_playlistinfo(self): + result1 = self.h.handle_request(u'playlistinfo "-1"') + result2 = self.h.handle_request(u'playlistinfo') + self.assertEqual(result1, result2) def test_playlistinfo_with_open_range(self): + # FIXME testing just ok is not enough result = self.h.handle_request(u'playlistinfo "10:"') self.assert_(u'OK' in result) def test_playlistinfo_with_closed_range(self): + # FIXME testing just ok is not enough result = self.h.handle_request(u'playlistinfo "10:20"') self.assert_(u'OK' in result) diff --git a/tests/utils_test.py b/tests/utils_test.py index 5ee8797b..59f77ec7 100644 --- a/tests/utils_test.py +++ b/tests/utils_test.py @@ -144,21 +144,21 @@ expected_albums = [Album(name='albumname', artists=expected_artists, num_tracks=2)] expected_tracks = [] -def generate_track(path): +def generate_track(path, ident): uri = path_to_uri(data_folder(path)) track = Track(name='trackname', artists=expected_artists, track_no=1, - album=expected_albums[0], length=4000, uri=uri) + album=expected_albums[0], length=4000, uri=uri, id=ident) expected_tracks.append(track) -generate_track('song1.mp3') -generate_track('song2.mp3') -generate_track('song3.mp3') -generate_track('subdir1/song4.mp3') -generate_track('subdir1/song5.mp3') -generate_track('subdir2/song6.mp3') -generate_track('subdir2/song7.mp3') -generate_track('subdir1/subsubdir/song8.mp3') -generate_track('subdir1/subsubdir/song9.mp3') +generate_track('song1.mp3', 6) +generate_track('song2.mp3', 7) +generate_track('song3.mp3', 8) +generate_track('subdir1/song4.mp3', 2) +generate_track('subdir1/song5.mp3', 3) +generate_track('subdir2/song6.mp3', 4) +generate_track('subdir2/song7.mp3', 5) +generate_track('subdir1/subsubdir/song8.mp3', 0) +generate_track('subdir1/subsubdir/song9.mp3', 1) class MPDTagCacheToTracksTest(unittest.TestCase): def test_emtpy_cache(self): @@ -169,7 +169,10 @@ class MPDTagCacheToTracksTest(unittest.TestCase): def test_simple_cache(self): tracks = parse_mpd_tag_cache(data_folder('simple_tag_cache'), data_folder('')) - self.assertEqual(expected_tracks[0], list(tracks)[0]) + uri = path_to_uri(data_folder('song1.mp3')) + track = Track(name='trackname', artists=expected_artists, track_no=1, + album=expected_albums[0], length=4000, uri=uri, id=0) + self.assertEqual(set([track]), tracks) def test_advanced_cache(self): tracks = parse_mpd_tag_cache(data_folder('advanced_tag_cache'), @@ -187,4 +190,4 @@ class MPDTagCacheToTracksTest(unittest.TestCase): tracks = parse_mpd_tag_cache(data_folder('blank_tag_cache'), data_folder('')) uri = path_to_uri(data_folder('song1.mp3')) - self.assertEqual(set([Track(uri=uri, length=4000)]), tracks) + self.assertEqual(set([Track(uri=uri, length=4000, id=0)]), tracks)