diff --git a/docs/changes.rst b/docs/changes.rst index ebd4512f..faefa541 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -12,6 +12,19 @@ v0.6.0 (in development) - Pykka 0.12.3 or greater is required. +- All config, data, and cache locations are now based on the XDG spec. + + - This means that your settings file will need to be moved from + ``~/.mopidy/settings.py`` to ``~/.config/mopidy/settings.py``. + - Your Spotify cache will now be stored in ``~/.cache/mopidy`` instead of + ``~/.mopidy/spotify_cache``, this matches Spotify's own behaviour for their + Linux client. + - The local backend's ``tag_cache`` should now be in + ``~/.local/share/mopidy/tag_cache``, likewise your playlists will be in + ``~/.local/share/mopidy/playlists``. + - The local client now tries to lookup where your music is via XDG, it will + fall-back to ``~/music`` or use whatever setting you set manually. + **Changes** - Replace :attr:`mopidy.backends.base.Backend.uri_handlers` with @@ -22,7 +35,7 @@ v0.6.0 (in development) ad hoc events the Last.fm scrobbler has already been using for some time. - Replaced all of the MPD network code that was provided by asyncore with custom stack. This change was made to facilitate the future support of the - `idle` command, and to reduce the number of event loops being used. + ``idle`` command, and to reduce the number of event loops being used. - Fix metadata update in Shoutcast streaming (Fixes: :issue:`122`) diff --git a/mopidy/__init__.py b/mopidy/__init__.py index 7b25c525..1d820fd0 100644 --- a/mopidy/__init__.py +++ b/mopidy/__init__.py @@ -3,10 +3,18 @@ import sys if not (2, 6) <= sys.version_info < (3,): sys.exit(u'Mopidy requires Python >= 2.6, < 3') +import glib +import os + from subprocess import PIPE, Popen VERSION = (0, 6, 0) +DATA_PATH = os.path.join(glib.get_user_data_dir(), 'mopidy') +CACHE_PATH = os.path.join(glib.get_user_cache_dir(), 'mopidy') +SETTINGS_PATH = os.path.join(glib.get_user_config_dir(), 'mopidy') +SETTINGS_FILE = os.path.join(SETTINGS_PATH, 'settings.py') + def get_version(): try: return get_git_version() diff --git a/mopidy/backends/local/__init__.py b/mopidy/backends/local/__init__.py index af80a8eb..e689f666 100644 --- a/mopidy/backends/local/__init__.py +++ b/mopidy/backends/local/__init__.py @@ -1,4 +1,5 @@ import glob +import glib import logging import os import shutil @@ -6,7 +7,7 @@ import shutil from pykka.actor import ThreadingActor from pykka.registry import ActorRegistry -from mopidy import settings +from mopidy import settings, DATA_PATH from mopidy.backends.base import (Backend, CurrentPlaylistController, LibraryController, BaseLibraryProvider, PlaybackController, BasePlaybackProvider, StoredPlaylistsController, @@ -18,6 +19,14 @@ from .translator import parse_m3u, parse_mpd_tag_cache logger = logging.getLogger(u'mopidy.backends.local') +DEFAULT_PLAYLIST_PATH = os.path.join(DATA_PATH, 'playlists') +DEFAULT_TAG_CACHE_FILE = os.path.join(DATA_PATH, 'tag_cache') +DEFAULT_MUSIC_PATH = glib.get_user_special_dir(glib.USER_DIRECTORY_MUSIC) + +if not DEFAULT_MUSIC_PATH or DEFAULT_MUSIC_PATH == os.path.expanduser(u'~'): + DEFAULT_MUSIC_PATH = os.path.expanduser(u'~/music') + + class LocalBackend(ThreadingActor, Backend): """ A backend for playing music from a local music archive. @@ -96,7 +105,7 @@ class LocalPlaybackProvider(BasePlaybackProvider): class LocalStoredPlaylistsProvider(BaseStoredPlaylistsProvider): def __init__(self, *args, **kwargs): super(LocalStoredPlaylistsProvider, self).__init__(*args, **kwargs) - self._folder = settings.LOCAL_PLAYLIST_PATH + self._folder = settings.LOCAL_PLAYLIST_PATH or DEFAULT_PLAYLIST_PATH self.refresh() def lookup(self, uri): @@ -173,8 +182,8 @@ class LocalLibraryProvider(BaseLibraryProvider): self.refresh() def refresh(self, uri=None): - tag_cache = settings.LOCAL_TAG_CACHE_FILE - music_folder = settings.LOCAL_MUSIC_PATH + tag_cache = settings.LOCAL_TAG_CACHE_FILE or DEFAULT_TAG_CACHE_FILE + music_folder = settings.LOCAL_MUSIC_PATH or DEFAULT_MUSIC_PATH tracks = parse_mpd_tag_cache(tag_cache, music_folder) diff --git a/mopidy/backends/spotify/session_manager.py b/mopidy/backends/spotify/session_manager.py index 2c6509ed..9c8853e6 100644 --- a/mopidy/backends/spotify/session_manager.py +++ b/mopidy/backends/spotify/session_manager.py @@ -1,3 +1,4 @@ +import glib import logging import os import threading @@ -6,7 +7,7 @@ from spotify.manager import SpotifySessionManager as PyspotifySessionManager from pykka.registry import ActorRegistry -from mopidy import get_version, settings +from mopidy import get_version, settings, CACHE_PATH from mopidy.backends.base import Backend from mopidy.backends.spotify import BITRATES from mopidy.backends.spotify.container_manager import SpotifyContainerManager @@ -21,9 +22,10 @@ logger = logging.getLogger('mopidy.backends.spotify.session_manager') # pylint: disable = R0901 # SpotifySessionManager: Too many ancestors (9/7) + class SpotifySessionManager(BaseThread, PyspotifySessionManager): - cache_location = settings.SPOTIFY_CACHE_PATH - settings_location = settings.SPOTIFY_CACHE_PATH + cache_location = settings.SPOTIFY_CACHE_PATH or CACHE_PATH + settings_location = settings.SPOTIFY_CACHE_PATH or CACHE_PATH appkey_file = os.path.join(os.path.dirname(__file__), 'spotify_appkey.key') user_agent = 'Mopidy %s' % get_version() diff --git a/mopidy/core.py b/mopidy/core.py index e831fc55..bf794655 100644 --- a/mopidy/core.py +++ b/mopidy/core.py @@ -1,5 +1,6 @@ import logging import optparse +import os import signal import sys @@ -20,7 +21,7 @@ sys.argv[1:] = gstreamer_args from pykka.registry import ActorRegistry from mopidy import (get_version, settings, OptionalDependencyError, - SettingsError) + SettingsError, DATA_PATH, SETTINGS_PATH, SETTINGS_FILE) from mopidy.gstreamer import GStreamer from mopidy.utils import get_class from mopidy.utils.log import setup_logging @@ -37,6 +38,7 @@ def main(): try: options = parse_options() setup_logging(options.verbosity_level, options.save_debug_log) + check_old_folders() setup_settings(options.interactive) setup_gstreamer() setup_mixer() @@ -79,9 +81,20 @@ def parse_options(): help='list current settings') return parser.parse_args(args=mopidy_args)[0] +def check_old_folders(): + old_settings_folder = os.path.expanduser(u'~/.mopidy') + + if not os.path.isdir(old_settings_folder): + return + + logger.warning(u'Old settings folder found at %s, settings.py should be ' + 'moved to %s, any cache data should be deleted. See release notes ' + 'for further instructions.', old_settings_folder, SETTINGS_PATH) + def setup_settings(interactive): - get_or_create_folder('~/.mopidy/') - get_or_create_file('~/.mopidy/settings.py') + get_or_create_folder(SETTINGS_PATH) + get_or_create_folder(DATA_PATH) + get_or_create_file(SETTINGS_FILE) try: settings.validate(interactive) except SettingsError, e: diff --git a/mopidy/settings.py b/mopidy/settings.py index a1edfdeb..70d71832 100644 --- a/mopidy/settings.py +++ b/mopidy/settings.py @@ -4,7 +4,7 @@ Available settings and their default values. .. warning:: Do *not* change settings directly in :mod:`mopidy.settings`. Instead, add a - file called ``~/.mopidy/settings.py`` and redefine settings there. + file called ``~/.config/mopidy/settings.py`` and redefine settings there. """ #: List of playback backends to use. See :mod:`mopidy.backends` for all @@ -79,8 +79,9 @@ LASTFM_PASSWORD = u'' #: #: Default:: #: -#: LOCAL_MUSIC_PATH = u'~/music' -LOCAL_MUSIC_PATH = u'~/music' +#: # Defaults to asking glib where music is stored, fallback is ~/music +#: LOCAL_MUSIC_PATH = None +LOCAL_MUSIC_PATH = None #: Path to playlist folder with m3u files for local music. #: @@ -88,8 +89,8 @@ LOCAL_MUSIC_PATH = u'~/music' #: #: Default:: #: -#: LOCAL_PLAYLIST_PATH = u'~/.mopidy/playlists' -LOCAL_PLAYLIST_PATH = u'~/.mopidy/playlists' +#: LOCAL_PLAYLIST_PATH = None # Implies $XDG_DATA_DIR/mopidy/playlists +LOCAL_PLAYLIST_PATH = None #: Path to tag cache for local music. #: @@ -97,8 +98,8 @@ LOCAL_PLAYLIST_PATH = u'~/.mopidy/playlists' #: #: Default:: #: -#: LOCAL_TAG_CACHE_FILE = u'~/.mopidy/tag_cache' -LOCAL_TAG_CACHE_FILE = u'~/.mopidy/tag_cache' +#: LOCAL_TAG_CACHE_FILE = None # Implies $XDG_DATA_DIR/mopidy/tag_cache +LOCAL_TAG_CACHE_FILE = None #: Sound mixer to use. See :mod:`mopidy.mixers` for all available mixers. #: @@ -238,7 +239,7 @@ SHOUTCAST_OUTPUT_ENCODER = u'lame mode=stereo bitrate=320' #: Path to the Spotify cache. #: #: Used by :mod:`mopidy.backends.spotify`. -SPOTIFY_CACHE_PATH = u'~/.mopidy/spotify_cache' +SPOTIFY_CACHE_PATH = None #: Your Spotify Premium username. #: diff --git a/mopidy/utils/settings.py b/mopidy/utils/settings.py index 3f7593af..fca4f337 100644 --- a/mopidy/utils/settings.py +++ b/mopidy/utils/settings.py @@ -2,12 +2,13 @@ from __future__ import absolute_import from copy import copy import getpass +import glib import logging import os from pprint import pformat import sys -from mopidy import SettingsError +from mopidy import SettingsError, SETTINGS_PATH, SETTINGS_FILE from mopidy.utils.log import indent logger = logging.getLogger('mopidy.utils.settings') @@ -20,11 +21,9 @@ class SettingsProxy(object): self.runtime = {} def _get_local_settings(self): - dotdir = os.path.expanduser(u'~/.mopidy/') - settings_file = os.path.join(dotdir, u'settings.py') - if not os.path.isfile(settings_file): + if not os.path.isfile(SETTINGS_FILE): return {} - sys.path.insert(0, dotdir) + sys.path.insert(0, SETTINGS_PATH) # pylint: disable = F0401 import settings as local_settings_module # pylint: enable = F0401