diff --git a/bin/mopidy b/bin/mopidy old mode 100644 new mode 100755 diff --git a/bin/mopidy-scan b/bin/mopidy-scan index 8534372c..84cfee57 100755 --- a/bin/mopidy-scan +++ b/bin/mopidy-scan @@ -17,15 +17,15 @@ if __name__ == '__main__': def debug(uri, error): print >> sys.stderr, 'Failed %s: %s' % (uri, error) - print >> sys.stderr, 'Scanning %s' % settings.LOCAL_MUSIC_FOLDER + print >> sys.stderr, 'Scanning %s' % settings.LOCAL_MUSIC_PATH - scanner = Scanner(settings.LOCAL_MUSIC_FOLDER, store, debug) + scanner = Scanner(settings.LOCAL_MUSIC_PATH, store, debug) scanner.start() print >> sys.stderr, 'Done' for a in tracks_to_tag_cache_format(tracks): if len(a) == 1: - print a[0] + print (u'%s' % a).encode('utf-8') else: - print u': '.join([unicode(b) for b in a]).encode('utf-8') + print (u'%s: %s' % a).encode('utf-8') diff --git a/docs/api/frontends.rst b/docs/api/frontends.rst index 2f20c72a..0c1e32a3 100644 --- a/docs/api/frontends.rst +++ b/docs/api/frontends.rst @@ -1,6 +1,6 @@ -*********************** -:mod:`mopidy.frontends` -*********************** +************ +Frontend API +************ A frontend may do whatever it wants to, including creating threads, opening TCP ports and exposing Mopidy for a type of clients. @@ -9,14 +9,6 @@ Frontends got one main limitation: they are restricted to passing messages through the ``core_queue`` for all communication with the rest of Mopidy. Thus, the frontend API is very small and reveals little of what a frontend may do. -.. automodule:: mopidy.frontends - :synopsis: Frontend API - :members: - - -Frontend API -============ - .. warning:: A stable frontend API is not available yet, as we've only implemented a diff --git a/docs/api/mixers.rst b/docs/api/mixers.rst index 1d9937ac..434637f3 100644 --- a/docs/api/mixers.rst +++ b/docs/api/mixers.rst @@ -1,6 +1,6 @@ -******************** -:mod:`mopidy.mixers` -******************** +********* +Mixer API +********* Mixers are responsible for controlling volume. Clients of the mixers will simply instantiate a mixer and read/write to the ``volume`` attribute:: @@ -24,10 +24,6 @@ enable one of the hardware device mixers, you must the set :attr:`mopidy.settings.MIXER` setting to point to one of the classes found below, and possibly add some extra settings required by the mixer you choose. - -Mixer API -========= - All mixers should subclass :class:`mopidy.mixers.BaseMixer` and override methods as described below. diff --git a/docs/api/models.rst b/docs/api/models.rst index 62e6f75a..ef11547e 100644 --- a/docs/api/models.rst +++ b/docs/api/models.rst @@ -1,6 +1,6 @@ -******************** -:mod:`mopidy.models` -******************** +*********** +Data models +*********** These immutable data models are used for all data transfer within the Mopidy backends and between the backends and the MPD frontend. All fields are optional diff --git a/docs/api/outputs.rst b/docs/api/outputs.rst index d8c2932e..5ef1606d 100644 --- a/docs/api/outputs.rst +++ b/docs/api/outputs.rst @@ -1,15 +1,17 @@ -********************* -:mod:`mopidy.outputs` -********************* +********** +Output API +********** Outputs are responsible for playing audio. +.. warning:: -Output API -========== + A stable output API is not available yet, as we've only implemented a + single output module. -A stable output API is not available yet, as we've only implemented a single -output module. +.. automodule:: mopidy.outputs.base + :synopsis: Base class for outputs + :members: Output implementations diff --git a/docs/api/settings.rst b/docs/api/settings.rst deleted file mode 100644 index cfc270d6..00000000 --- a/docs/api/settings.rst +++ /dev/null @@ -1,27 +0,0 @@ -********************** -:mod:`mopidy.settings` -********************** - - -Changing settings -================= - -For any Mopidy installation you will need to change at least a couple of -settings. To do this, create a new file in the ``~/.mopidy/`` directory -named ``settings.py`` and add settings you need to change from their defaults -there. - -A complete ``~/.mopidy/settings.py`` may look like this:: - - MPD_SERVER_HOSTNAME = u'::' - SPOTIFY_USERNAME = u'alice' - SPOTIFY_PASSWORD = u'mysecret' - - -Available settings -================== - -.. automodule:: mopidy.settings - :synopsis: Available settings and their default values - :members: - :undoc-members: diff --git a/docs/settings.rst b/docs/settings.rst index a7638b4e..532f52cf 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -2,13 +2,31 @@ Settings ******** +Mopidy has lots of settings. Luckily, you only need to change a few, and stay +ignorant of the rest. Below you can find guides for typical configuration +changes you may want to do, and a complete listing of available settings. + + +Changing settings +================= + Mopidy reads settings from the file ``~/.mopidy/settings.py``, where ``~`` means your *home directory*. If your username is ``alice`` and you are running Linux, the settings file should probably be at ``/home/alice/.mopidy/settings.py``. -You can either create this file yourself, or run the ``mopidy`` command, and it -will create an empty settings file for you. +You can either create the settings file yourself, or run the ``mopidy`` +command, and it will create an empty settings file for you. + +When you have created the settings file, open it in a text editor, and add +settings you want to change. If you want to keep the default value for setting, +you should *not* redefine it in your own settings file. + +A complete ``~/.mopidy/settings.py`` may look as simple as this:: + + MPD_SERVER_HOSTNAME = u'::' + SPOTIFY_USERNAME = u'alice' + SPOTIFY_PASSWORD = u'mysecret' Music from Spotify @@ -47,12 +65,12 @@ Generating a tag cache Previously the local storage backend relied purely on ``tag_cache`` files generated by the original MPD server. To remedy this the command :command:`mopidy-scan` has been created. The program will scan your current -:attr:`mopidy.settings.LOCAL_MUSIC_FOLDER` and build a MPD compatible +:attr:`mopidy.settings.LOCAL_MUSIC_PATH` and build a MPD compatible ``tag_cache``. To make a ``tag_cache`` of your local music available for Mopidy: -#. Ensure that :attr:`mopidy.settings.LOCAL_MUSIC_FOLDER` points to where your +#. Ensure that :attr:`mopidy.settings.LOCAL_MUSIC_PATH` points to where your music is located. Check the current setting by running:: mopidy --list-settings @@ -64,7 +82,7 @@ To make a ``tag_cache`` of your local music available for Mopidy: mopidy-scan > tag_cache #. Move the ``tag_cache`` file to the location - :attr:`mopidy.settings.LOCAL_TAG_CACHE` is set to, or change the setting to + :attr:`mopidy.settings.LOCAL_TAG_CACHE_FILE` is set to, or change the setting to point to where your ``tag_cache`` file is. #. Start Mopidy, find the music library in a client, and play some local music! @@ -88,3 +106,12 @@ file:: LASTFM_USERNAME = u'myusername' LASTFM_PASSWORD = u'mysecret' + + +Available settings +================== + +.. automodule:: mopidy.settings + :synopsis: Available settings and their default values + :members: + :undoc-members: diff --git a/mopidy/backends/dummy/__init__.py b/mopidy/backends/dummy/__init__.py index 6549b9ec..6b259338 100644 --- a/mopidy/backends/dummy/__init__.py +++ b/mopidy/backends/dummy/__init__.py @@ -93,7 +93,7 @@ class DummyStoredPlaylistsProvider(BaseStoredPlaylistsProvider): def rename(self, playlist, new_name): self._playlists[self._playlists.index(playlist)] = \ - playlist.with_(name=new_name) + playlist.copy(name=new_name) def save(self, playlist): self._playlists.append(playlist) diff --git a/mopidy/backends/libspotify/__init__.py b/mopidy/backends/libspotify/__init__.py index dbbd4804..98b9e884 100644 --- a/mopidy/backends/libspotify/__init__.py +++ b/mopidy/backends/libspotify/__init__.py @@ -19,7 +19,7 @@ class LibspotifyBackend(BaseBackend): **Settings:** - - :attr:`mopidy.settings.SPOTIFY_LIB_CACHE` + - :attr:`mopidy.settings.SPOTIFY_CACHE_PATH` - :attr:`mopidy.settings.SPOTIFY_USERNAME` - :attr:`mopidy.settings.SPOTIFY_PASSWORD` diff --git a/mopidy/backends/libspotify/session_manager.py b/mopidy/backends/libspotify/session_manager.py index 45841350..5831b713 100644 --- a/mopidy/backends/libspotify/session_manager.py +++ b/mopidy/backends/libspotify/session_manager.py @@ -15,8 +15,8 @@ logger = logging.getLogger('mopidy.backends.libspotify.session_manager') # LibspotifySessionManager: Too many ancestors (9/7) class LibspotifySessionManager(SpotifySessionManager, BaseThread): - cache_location = os.path.expanduser(settings.SPOTIFY_LIB_CACHE) - settings_location = os.path.expanduser(settings.SPOTIFY_LIB_CACHE) + cache_location = settings.SPOTIFY_CACHE_PATH + settings_location = settings.SPOTIFY_CACHE_PATH appkey_file = os.path.join(os.path.dirname(__file__), 'spotify_appkey.key') user_agent = 'Mopidy %s' % get_version() diff --git a/mopidy/backends/local/__init__.py b/mopidy/backends/local/__init__.py index b2c36b72..cf9cbd60 100644 --- a/mopidy/backends/local/__init__.py +++ b/mopidy/backends/local/__init__.py @@ -24,9 +24,9 @@ class LocalBackend(BaseBackend): **Settings:** - - :attr:`mopidy.settings.LOCAL_MUSIC_FOLDER` - - :attr:`mopidy.settings.LOCAL_PLAYLIST_FOLDER` - - :attr:`mopidy.settings.LOCAL_TAG_CACHE` + - :attr:`mopidy.settings.LOCAL_MUSIC_PATH` + - :attr:`mopidy.settings.LOCAL_PLAYLIST_PATH` + - :attr:`mopidy.settings.LOCAL_TAG_CACHE_FILE` """ def __init__(self, *args, **kwargs): @@ -79,7 +79,7 @@ class LocalPlaybackProvider(BasePlaybackProvider): class LocalStoredPlaylistsProvider(BaseStoredPlaylistsProvider): def __init__(self, *args, **kwargs): super(LocalStoredPlaylistsProvider, self).__init__(*args, **kwargs) - self._folder = os.path.expanduser(settings.LOCAL_PLAYLIST_FOLDER) + self._folder = settings.LOCAL_PLAYLIST_PATH self.refresh() def lookup(self, uri): @@ -129,7 +129,7 @@ class LocalStoredPlaylistsProvider(BaseStoredPlaylistsProvider): src = os.path.join(self._folder, playlist.name + '.m3u') dst = os.path.join(self._folder, name + '.m3u') - renamed = playlist.with_(name=name) + renamed = playlist.copy(name=name) index = self._playlists.index(playlist) self._playlists[index] = renamed @@ -156,8 +156,8 @@ class LocalLibraryController(BaseLibraryController): self.refresh() def refresh(self, uri=None): - tag_cache = os.path.expanduser(settings.LOCAL_TAG_CACHE) - music_folder = os.path.expanduser(settings.LOCAL_MUSIC_FOLDER) + tag_cache = settings.LOCAL_TAG_CACHE_FILE + music_folder = settings.LOCAL_MUSIC_PATH tracks = parse_mpd_tag_cache(tag_cache, music_folder) diff --git a/mopidy/backends/local/translator.py b/mopidy/backends/local/translator.py index 87ea15df..26c2ad6b 100644 --- a/mopidy/backends/local/translator.py +++ b/mopidy/backends/local/translator.py @@ -84,7 +84,7 @@ def parse_mpd_tag_cache(tag_cache, music_dir=''): _convert_mpd_data(current, tracks, music_dir) current.clear() - current[key.lower()] = value + current[key.lower()] = value.decode('utf-8') _convert_mpd_data(current, tracks, music_dir) @@ -106,6 +106,7 @@ def _convert_mpd_data(data, tracks, music_dir): track_kwargs['artists'] = [artist] album_kwargs['artists'] = [artist] + # FIXME Newer mpd tag caches support albumartist names if 'album' in data: album_kwargs['name'] = data['album'] album = Album(**album_kwargs) @@ -114,11 +115,15 @@ def _convert_mpd_data(data, tracks, music_dir): if 'title' in data: track_kwargs['name'] = data['title'] + # FIXME what if file is uri - generated tag cache needs to allways make + # LOCAL_MUSIC_PATH relative paths or this code must handle uris if data['file'][0] == '/': path = data['file'][1:] else: path = data['file'] + # FIXME newer mpd tag caches provide musicbrainz ids + track_kwargs['uri'] = path_to_uri(music_dir, path) track_kwargs['length'] = int(data.get('time', 0)) * 1000 diff --git a/mopidy/frontends/mpd/translator.py b/mopidy/frontends/mpd/translator.py index 2b1adf50..e15e1ba5 100644 --- a/mopidy/frontends/mpd/translator.py +++ b/mopidy/frontends/mpd/translator.py @@ -6,7 +6,7 @@ from mopidy.utils.path import mtime as get_mtime from mopidy.frontends.mpd import protocol from mopidy.utils.path import path_to_uri, uri_to_path, split_path -def track_to_mpd_format(track, position=None, cpid=None, key=False, mtime=False): +def track_to_mpd_format(track, position=None, cpid=None): """ Format track for output to MPD client. @@ -41,10 +41,6 @@ def track_to_mpd_format(track, position=None, cpid=None, key=False, mtime=False) if position is not None and cpid is not None: result.append(('Pos', position)) result.append(('Id', cpid)) - if key and track.uri: - result.insert(0, ('key', os.path.basename(uri_to_path(track.uri)))) - if mtime and track.uri: - result.append(('mtime', get_mtime(uri_to_path(track.uri)))) return result MPD_KEY_ORDER = ''' @@ -127,9 +123,11 @@ def tracks_to_tag_cache_format(tracks): return result def _add_to_tag_cache(result, folders, files): + music_folder = settings.LOCAL_MUSIC_PATH + regexp = '^' + re.escape(music_folder).rstrip('/') + '/?' + for path, entry in folders.items(): name = os.path.split(path)[1] - music_folder = os.path.expanduser(settings.LOCAL_MUSIC_FOLDER) mtime = get_mtime(os.path.join(music_folder, path)) result.append(('directory', path)) result.append(('mtime', mtime)) @@ -139,8 +137,12 @@ def _add_to_tag_cache(result, folders, files): result.append(('songList begin',)) for track in files: - track_result = track_to_mpd_format(track, key=True, mtime=True) - track_result = order_mpd_track_info(track_result) + track_result = dict(track_to_mpd_format(track)) + path = uri_to_path(track_result['file']) + track_result['mtime'] = get_mtime(path) + track_result['file'] = re.sub(regexp, '', path) + track_result['key'] = os.path.basename(track_result['file']) + track_result = order_mpd_track_info(track_result.items()) result.extend(track_result) result.append(('songList end',)) @@ -150,7 +152,7 @@ def tracks_to_directory_tree(tracks): path = u'' current = directories - local_folder = os.path.expanduser(settings.LOCAL_MUSIC_FOLDER) + local_folder = settings.LOCAL_MUSIC_PATH track_path = uri_to_path(track.uri) track_path = re.sub('^' + re.escape(local_folder), '', track_path) track_dir = os.path.dirname(track_path) diff --git a/mopidy/models.py b/mopidy/models.py index c5877657..e691ccb7 100644 --- a/mopidy/models.py +++ b/mopidy/models.py @@ -179,7 +179,7 @@ class Playlist(ImmutableObject): def mpd_format(self, *args, **kwargs): return translator.playlist_to_mpd_format(self, *args, **kwargs) - def with_(self, uri=None, name=None, tracks=None, last_modified=None): + def copy(self, uri=None, name=None, tracks=None, last_modified=None): """ Create a new playlist object with the given values. The values that are not given are taken from the object the method is called on. diff --git a/mopidy/scanner.py b/mopidy/scanner.py index 436598bd..4ccccbdb 100644 --- a/mopidy/scanner.py +++ b/mopidy/scanner.py @@ -5,7 +5,6 @@ import pygst pygst.require('0.10') import gst -from os.path import abspath import datetime import sys import threading diff --git a/mopidy/settings.py b/mopidy/settings.py index c9d7b9fc..4f60ee99 100644 --- a/mopidy/settings.py +++ b/mopidy/settings.py @@ -77,8 +77,8 @@ LASTFM_PASSWORD = u'' #: #: Default:: #: -#: LOCAL_MUSIC_FOLDER = u'~/music' -LOCAL_MUSIC_FOLDER = u'~/music' +#: LOCAL_MUSIC_PATH = u'~/music' +LOCAL_MUSIC_PATH = u'~/music' #: Path to playlist folder with m3u files for local music. #: @@ -86,8 +86,8 @@ LOCAL_MUSIC_FOLDER = u'~/music' #: #: Default:: #: -#: LOCAL_PLAYLIST_FOLDER = u'~/.mopidy/playlists' -LOCAL_PLAYLIST_FOLDER = u'~/.mopidy/playlists' +#: LOCAL_PLAYLIST_PATH = u'~/.mopidy/playlists' +LOCAL_PLAYLIST_PATH = u'~/.mopidy/playlists' #: Path to tag cache for local music. #: @@ -95,8 +95,8 @@ LOCAL_PLAYLIST_FOLDER = u'~/.mopidy/playlists' #: #: Default:: #: -#: LOCAL_TAG_CACHE = u'~/.mopidy/tag_cache' -LOCAL_TAG_CACHE = u'~/.mopidy/tag_cache' +#: LOCAL_TAG_CACHE_FILE = u'~/.mopidy/tag_cache' +LOCAL_TAG_CACHE_FILE = u'~/.mopidy/tag_cache' #: Sound mixer to use. See :mod:`mopidy.mixers` for all available mixers. #: @@ -172,7 +172,7 @@ MPD_SERVER_PORT = 6600 #: Path to the libspotify cache. #: #: Used by :mod:`mopidy.backends.libspotify`. -SPOTIFY_LIB_CACHE = u'~/.mopidy/libspotify_cache' +SPOTIFY_CACHE_PATH = u'~/.mopidy/libspotify_cache' #: Your Spotify Premium username. #: diff --git a/mopidy/utils/path.py b/mopidy/utils/path.py index b3669e38..f25d754a 100644 --- a/mopidy/utils/path.py +++ b/mopidy/utils/path.py @@ -22,7 +22,6 @@ def get_or_create_file(filename): def path_to_uri(*paths): path = os.path.join(*paths) - #path = os.path.expanduser(path) # FIXME Waiting for test case? path = path.encode('utf-8') if sys.platform == 'win32': return 'file:' + urllib.pathname2url(path) @@ -46,16 +45,13 @@ def split_path(path): return parts def find_files(path): - path = os.path.expanduser(path) if os.path.isfile(path): - filename = os.path.abspath(path) - if not isinstance(filename, unicode): - filename = filename.decode('utf-8') - yield filename + if not isinstance(path, unicode): + path = path.decode('utf-8') + yield path else: for dirpath, dirnames, filenames in os.walk(path): for filename in filenames: - dirpath = os.path.abspath(dirpath) filename = os.path.join(dirpath, filename) if not isinstance(filename, unicode): filename = filename.decode('utf-8') diff --git a/mopidy/utils/settings.py b/mopidy/utils/settings.py index ac75cb70..2ec0f716 100644 --- a/mopidy/utils/settings.py +++ b/mopidy/utils/settings.py @@ -51,6 +51,9 @@ class SettingsProxy(object): value = self.current[attr] if type(value) != bool and not value: raise SettingsError(u'Setting "%s" is empty.' % attr) + if attr.endswith('_PATH') or attr.endswith('_FILE'): + value = os.path.expanduser(value) + value = os.path.abspath(value) return value def __setattr__(self, attr, value): @@ -94,10 +97,14 @@ def validate_settings(defaults, settings): 'DUMP_LOG_FILENAME': 'DEBUG_LOG_FILENAME', 'DUMP_LOG_FORMAT': 'DEBUG_LOG_FORMAT', 'FRONTEND': 'FRONTENDS', + 'LOCAL_MUSIC_FOLDER': 'LOCAL_MUSIC_PATH', + 'LOCAL_PLAYLIST_FOLDER': 'LOCAL_PLAYLIST_PATH', + 'LOCAL_TAG_CACHE': 'LOCAL_TAG_CACHE_FILE', 'SERVER': None, 'SERVER_HOSTNAME': 'MPD_SERVER_HOSTNAME', 'SERVER_PORT': 'MPD_SERVER_PORT', 'SPOTIFY_LIB_APPKEY': None, + 'SPOTIFY_LIB_CACHE': 'SPOTIFY_CACHE_PATH', } for setting, value in settings.iteritems(): diff --git a/tests/backends/base/stored_playlists.py b/tests/backends/base/stored_playlists.py index ef5806ef..5bcd322c 100644 --- a/tests/backends/base/stored_playlists.py +++ b/tests/backends/base/stored_playlists.py @@ -10,9 +10,9 @@ from tests import SkipTest, data_folder class BaseStoredPlaylistsControllerTest(object): def setUp(self): - settings.LOCAL_PLAYLIST_FOLDER = tempfile.mkdtemp() - settings.LOCAL_TAG_CACHE = data_folder('library_tag_cache') - settings.LOCAL_MUSIC_FOLDER = data_folder('') + settings.LOCAL_PLAYLIST_PATH = tempfile.mkdtemp() + settings.LOCAL_TAG_CACHE_FILE = data_folder('library_tag_cache') + settings.LOCAL_MUSIC_PATH = data_folder('') self.backend = self.backend_class(mixer_class=DummyMixer) self.stored = self.backend.stored_playlists @@ -20,8 +20,8 @@ class BaseStoredPlaylistsControllerTest(object): def tearDown(self): self.backend.destroy() - if os.path.exists(settings.LOCAL_PLAYLIST_FOLDER): - shutil.rmtree(settings.LOCAL_PLAYLIST_FOLDER) + if os.path.exists(settings.LOCAL_PLAYLIST_PATH): + shutil.rmtree(settings.LOCAL_PLAYLIST_PATH) settings.runtime.clear() diff --git a/tests/backends/local/library_test.py b/tests/backends/local/library_test.py index c0605ef2..34465d09 100644 --- a/tests/backends/local/library_test.py +++ b/tests/backends/local/library_test.py @@ -17,8 +17,8 @@ class LocalLibraryControllerTest(BaseLibraryControllerTest, unittest.TestCase): backend_class = LocalBackend def setUp(self): - settings.LOCAL_TAG_CACHE = data_folder('library_tag_cache') - settings.LOCAL_MUSIC_FOLDER = data_folder('') + settings.LOCAL_TAG_CACHE_FILE = data_folder('library_tag_cache') + settings.LOCAL_MUSIC_PATH = data_folder('') super(LocalLibraryControllerTest, self).setUp() diff --git a/tests/backends/local/stored_playlists_test.py b/tests/backends/local/stored_playlists_test.py index bb03f997..4db9e1e2 100644 --- a/tests/backends/local/stored_playlists_test.py +++ b/tests/backends/local/stored_playlists_test.py @@ -25,13 +25,13 @@ class LocalStoredPlaylistsControllerTest(BaseStoredPlaylistsControllerTest, backend_class = LocalBackend def test_created_playlist_is_persisted(self): - path = os.path.join(settings.LOCAL_PLAYLIST_FOLDER, 'test.m3u') + path = os.path.join(settings.LOCAL_PLAYLIST_PATH, '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.LOCAL_PLAYLIST_FOLDER, 'test2.m3u') + path = os.path.join(settings.LOCAL_PLAYLIST_PATH, 'test2.m3u') self.assert_(not os.path.exists(path)) self.stored.save(Playlist(name='test2')) self.assert_(os.path.exists(path)) @@ -39,13 +39,13 @@ class LocalStoredPlaylistsControllerTest(BaseStoredPlaylistsControllerTest, def test_deleted_playlist_get_removed(self): playlist = self.stored.create('test') self.stored.delete(playlist) - path = os.path.join(settings.LOCAL_PLAYLIST_FOLDER, 'test.m3u') + path = os.path.join(settings.LOCAL_PLAYLIST_PATH, '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.LOCAL_PLAYLIST_FOLDER, 'test.m3u') - file2 = os.path.join(settings.LOCAL_PLAYLIST_FOLDER, 'test2.m3u') + file1 = os.path.join(settings.LOCAL_PLAYLIST_PATH, 'test.m3u') + file2 = os.path.join(settings.LOCAL_PLAYLIST_PATH, 'test2.m3u') self.assert_(not os.path.exists(file2)) self.stored.rename(playlist, 'test2') self.assert_(not os.path.exists(file1)) @@ -55,7 +55,7 @@ class LocalStoredPlaylistsControllerTest(BaseStoredPlaylistsControllerTest, track = Track(uri=generate_song(1)) uri = track.uri[len('file://'):] playlist = Playlist(tracks=[track], name='test') - path = os.path.join(settings.LOCAL_PLAYLIST_FOLDER, 'test.m3u') + path = os.path.join(settings.LOCAL_PLAYLIST_PATH, 'test.m3u') self.stored.save(playlist) diff --git a/tests/backends/local/translator_test.py b/tests/backends/local/translator_test.py index a9fe58d8..2f97e45c 100644 --- a/tests/backends/local/translator_test.py +++ b/tests/backends/local/translator_test.py @@ -116,7 +116,16 @@ class MPDTagCacheToTracksTest(unittest.TestCase): self.assertEqual(set(expected_tracks), tracks) def test_unicode_cache(self): - raise SkipTest + tracks = parse_mpd_tag_cache(data_folder('utf8_tag_cache'), + data_folder('')) + + uri = path_to_uri(data_folder('song1.mp3')) + artists = [Artist(name=u'æøå')] + album = Album(name=u'æøå', artists=artists) + track = Track(uri=uri, name=u'æøå', artists=artists, + album=album, length=4000) + + self.assertEqual(track, list(tracks)[0]) def test_misencoded_cache(self): # FIXME not sure if this can happen diff --git a/tests/data/utf8_tag_cache b/tests/data/utf8_tag_cache new file mode 100644 index 00000000..6642ec77 --- /dev/null +++ b/tests/data/utf8_tag_cache @@ -0,0 +1,13 @@ +info_begin +mpd_version: 0.14.2 +fs_charset: UTF-8 +info_end +songList begin +key: song1.mp3 +file: /song1.mp3 +Time: 4 +Artist: æøå +Title: æøå +Album: æøå +mtime: 1272319626 +songList end diff --git a/tests/frontends/mpd/serializer_test.py b/tests/frontends/mpd/serializer_test.py index 8e8a5d21..77a25e15 100644 --- a/tests/frontends/mpd/serializer_test.py +++ b/tests/frontends/mpd/serializer_test.py @@ -3,7 +3,7 @@ import os import unittest from mopidy import settings -from mopidy.utils.path import mtime +from mopidy.utils.path import mtime, uri_to_path from mopidy.frontends.mpd import translator, protocol from mopidy.models import Album, Artist, Playlist, Track @@ -11,7 +11,7 @@ from tests import data_folder, SkipTest class TrackMpdFormatTest(unittest.TestCase): def setUp(self): - settings.LOCAL_MUSIC_FOLDER = '/dir/subdir' + settings.LOCAL_MUSIC_PATH = '/dir/subdir' mtime.set_fake_time(1234567) def tearDown(self): @@ -42,21 +42,6 @@ class TrackMpdFormatTest(unittest.TestCase): self.assert_(('Pos', 1) in result) self.assert_(('Id', 2) in result) - def test_track_to_mpd_format_with_key(self): - track = Track(uri='file:///dir/subdir/file.mp3') - result = translator.track_to_mpd_format(track, key=True) - self.assert_(('key', 'file.mp3') in result) - - def test_track_to_mpd_format_with_key_not_uri_encoded(self): - track = Track(uri='file:///dir/subdir/file%20test.mp3') - result = translator.track_to_mpd_format(track, key=True) - self.assert_(('key', 'file test.mp3') in result) - - def test_track_to_mpd_format_with_mtime(self): - uri = translator.path_to_uri(data_folder('blank.mp3')) - result = translator.track_to_mpd_format(Track(uri=uri), mtime=True) - self.assert_(('mtime', 1234567) in result) - def test_track_to_mpd_format_for_nonempty_track(self): track = Track( uri=u'a uri', @@ -104,7 +89,7 @@ class PlaylistMpdFormatTest(unittest.TestCase): class TracksToTagCacheFormatTest(unittest.TestCase): def setUp(self): - settings.LOCAL_MUSIC_FOLDER = '/dir/subdir' + settings.LOCAL_MUSIC_PATH = '/dir/subdir' mtime.set_fake_time(1234567) def tearDown(self): @@ -112,8 +97,13 @@ class TracksToTagCacheFormatTest(unittest.TestCase): mtime.undo_fake() def translate(self, track): - result = translator.track_to_mpd_format(track, key=True, mtime=True) - return translator.order_mpd_track_info(result) + folder = settings.LOCAL_MUSIC_PATH + result = dict(translator.track_to_mpd_format(track)) + result['file'] = uri_to_path(result['file']) + result['file'] = result['file'][len(folder)+1:] + result['key'] = os.path.basename(result['file']) + result['mtime'] = mtime('') + return translator.order_mpd_track_info(result.items()) def consume_headers(self, result): self.assertEqual(('info_begin',), result[0]) @@ -279,7 +269,7 @@ class TracksToTagCacheFormatTest(unittest.TestCase): class TracksToDirectoryTreeTest(unittest.TestCase): def setUp(self): - settings.LOCAL_MUSIC_FOLDER = '/root/' + settings.LOCAL_MUSIC_PATH = '/root/' def tearDown(self): settings.runtime.clear() diff --git a/tests/models_test.py b/tests/models_test.py index ab7bc793..1ccf16ea 100644 --- a/tests/models_test.py +++ b/tests/models_test.py @@ -398,7 +398,7 @@ class PlaylistTest(unittest.TestCase): last_modified = dt.datetime.now() playlist = Playlist(uri=u'an uri', name=u'a name', tracks=tracks, last_modified=last_modified) - new_playlist = playlist.with_(uri=u'another uri') + new_playlist = playlist.copy(uri=u'another uri') self.assertEqual(new_playlist.uri, u'another uri') self.assertEqual(new_playlist.name, u'a name') self.assertEqual(new_playlist.tracks, tracks) @@ -409,7 +409,7 @@ class PlaylistTest(unittest.TestCase): last_modified = dt.datetime.now() playlist = Playlist(uri=u'an uri', name=u'a name', tracks=tracks, last_modified=last_modified) - new_playlist = playlist.with_(name=u'another name') + new_playlist = playlist.copy(name=u'another name') self.assertEqual(new_playlist.uri, u'an uri') self.assertEqual(new_playlist.name, u'another name') self.assertEqual(new_playlist.tracks, tracks) @@ -421,7 +421,7 @@ class PlaylistTest(unittest.TestCase): playlist = Playlist(uri=u'an uri', name=u'a name', tracks=tracks, last_modified=last_modified) new_tracks = [Track(), Track()] - new_playlist = playlist.with_(tracks=new_tracks) + new_playlist = playlist.copy(tracks=new_tracks) self.assertEqual(new_playlist.uri, u'an uri') self.assertEqual(new_playlist.name, u'a name') self.assertEqual(new_playlist.tracks, new_tracks) @@ -433,7 +433,7 @@ class PlaylistTest(unittest.TestCase): new_last_modified = last_modified + dt.timedelta(1) playlist = Playlist(uri=u'an uri', name=u'a name', tracks=tracks, last_modified=last_modified) - new_playlist = playlist.with_(last_modified=new_last_modified) + new_playlist = playlist.copy(last_modified=new_last_modified) self.assertEqual(new_playlist.uri, u'an uri') self.assertEqual(new_playlist.name, u'a name') self.assertEqual(new_playlist.tracks, tracks) diff --git a/tests/utils/path_test.py b/tests/utils/path_test.py index 758a09ab..4366305c 100644 --- a/tests/utils/path_test.py +++ b/tests/utils/path_test.py @@ -34,9 +34,6 @@ class GetOrCreateFolderTest(unittest.TestCase): self.assert_(os.path.isdir(self.parent)) self.assertEqual(created, self.parent) - def test_that_userfolder_is_expanded(self): - raise SkipTest # Not sure how to safely test this - class PathToFileURITest(unittest.TestCase): def test_simple_path(self): @@ -139,9 +136,6 @@ class FindFilesTest(unittest.TestCase): self.assert_(is_unicode(name), '%s is not unicode object' % repr(name)) - def test_expanduser(self): - raise SkipTest - class MtimeTest(unittest.TestCase): def tearDown(self): diff --git a/tests/utils/settings_test.py b/tests/utils/settings_test.py index 0c06ae5c..cef0069d 100644 --- a/tests/utils/settings_test.py +++ b/tests/utils/settings_test.py @@ -1,3 +1,4 @@ +import os import unittest from mopidy import settings as default_settings_module @@ -65,3 +66,37 @@ class SettingsProxyTest(unittest.TestCase): def test_runtime_value_included_in_current(self): self.settings.TEST = 'test' self.assertEqual(self.settings.current['TEST'], 'test') + + def test_value_ending_in_path_is_expanded(self): + self.settings.TEST_PATH = '~/test' + acctual = self.settings.TEST_PATH + expected = os.path.expanduser('~/test') + self.assertEqual(acctual, expected) + + def test_value_ending_in_path_is_absolute(self): + self.settings.TEST_PATH = './test' + acctual = self.settings.TEST_PATH + expected = os.path.abspath('./test') + self.assertEqual(acctual, expected) + + def test_value_ending_in_file_is_expanded(self): + self.settings.TEST_FILE = '~/test' + acctual = self.settings.TEST_FILE + expected = os.path.expanduser('~/test') + self.assertEqual(acctual, expected) + + def test_value_ending_in_file_is_absolute(self): + self.settings.TEST_FILE = './test' + acctual = self.settings.TEST_FILE + expected = os.path.abspath('./test') + self.assertEqual(acctual, expected) + + def test_value_not_ending_in_path_or_file_is_not_expanded(self): + self.settings.TEST = '~/test' + acctual = self.settings.TEST + self.assertEqual(acctual, '~/test') + + def test_value_not_ending_in_path_or_file_is_not_absolute(self): + self.settings.TEST = './test' + acctual = self.settings.TEST + self.assertEqual(acctual, './test')