Merge branch 'develop' into feature/multi-backend
Conflicts: mopidy/backends/local/__init__.py
This commit is contained in:
commit
0b681a4e4c
0
bin/mopidy
Normal file → Executable file
0
bin/mopidy
Normal file → Executable file
@ -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')
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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:
|
||||
@ -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:
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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`
|
||||
|
||||
|
||||
@ -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()
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -5,7 +5,6 @@ import pygst
|
||||
pygst.require('0.10')
|
||||
import gst
|
||||
|
||||
from os.path import abspath
|
||||
import datetime
|
||||
import sys
|
||||
import threading
|
||||
|
||||
@ -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.
|
||||
#:
|
||||
|
||||
@ -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')
|
||||
|
||||
@ -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():
|
||||
|
||||
@ -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()
|
||||
|
||||
|
||||
@ -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()
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
13
tests/data/utf8_tag_cache
Normal file
13
tests/data/utf8_tag_cache
Normal file
@ -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
|
||||
@ -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()
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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')
|
||||
|
||||
Loading…
Reference in New Issue
Block a user