From d8e0099ff45a0ba93750dffd02284ee0e08df4d9 Mon Sep 17 00:00:00 2001 From: tom roth Date: Tue, 7 Jul 2015 08:01:15 +0200 Subject: [PATCH] file-browser: Changed as discussed in PR 1207 --- docs/ext/files.rst | 4 +-- mopidy/files/__init__.py | 2 +- mopidy/files/ext.conf | 4 +-- mopidy/files/library.py | 56 +++++++++++++++++++++------------------- 4 files changed, 34 insertions(+), 32 deletions(-) diff --git a/docs/ext/files.rst b/docs/ext/files.rst index 4a8e741e..42120807 100644 --- a/docs/ext/files.rst +++ b/docs/ext/files.rst @@ -24,10 +24,10 @@ See :ref:`config` for general help on configuring Mopidy. If the files extension should be enabled or not. -.. confval:: files/media_dir +.. confval:: files/media_dirs A list of directories to be browsable. - Optionally the path can be followed by | and a name that will be shown for that path. + Optionally the path can be followed by ``|`` and a name that will be shown for that path. .. confval:: files/show_dotfiles diff --git a/mopidy/files/__init__.py b/mopidy/files/__init__.py index 90ebf7f8..d547b256 100644 --- a/mopidy/files/__init__.py +++ b/mopidy/files/__init__.py @@ -21,7 +21,7 @@ class Extension(ext.Extension): def get_config_schema(self): schema = super(Extension, self).get_config_schema() - schema['media_dir'] = config.List(optional=True) + schema['media_dirs'] = config.List(optional=True) schema['show_dotfiles'] = config.Boolean(optional=True) schema['follow_symlinks'] = config.Boolean(optional=True) schema['metadata_timeout'] = config.Integer(optional=True) diff --git a/mopidy/files/ext.conf b/mopidy/files/ext.conf index 836db665..afdd1183 100644 --- a/mopidy/files/ext.conf +++ b/mopidy/files/ext.conf @@ -1,8 +1,8 @@ [files] enabled = true -media_dir = +media_dirs = $XDG_MUSIC_DIR|Music ~/|Home show_dotfiles = false -follow_symlinks = true +follow_symlinks = false metadata_timeout = 1000 diff --git a/mopidy/files/library.py b/mopidy/files/library.py index bea8c062..47c58cda 100644 --- a/mopidy/files/library.py +++ b/mopidy/files/library.py @@ -3,7 +3,6 @@ from __future__ import unicode_literals import logging import operator import os -import stat import sys import urllib2 @@ -27,7 +26,7 @@ class FilesLibraryProvider(backend.LibraryProvider): local_path = self._media_dirs[0]['path'] uri = path.path_to_uri(local_path) else: - uri = u'file:root' + uri = 'file:root' return models.Ref.directory(name='Files', uri=uri) def __init__(self, backend, config): @@ -39,49 +38,52 @@ class FilesLibraryProvider(backend.LibraryProvider): timeout=config['files']['metadata_timeout']) def browse(self, uri): - logger.debug('browse called with uri %s', uri) + logger.debug('Browsing files at: %s', uri) result = [] local_path = path.uri_to_path(uri) if local_path == 'root': return list(self._get_media_dirs_refs()) - for name in os.listdir(local_path): - if not self._is_in_basedir(local_path): - logger.warn(u'Not in base_dir: %s', local_path) + for dir_entry in os.listdir(local_path): + child_path = os.path.join(local_path, dir_entry) + uri = path.path_to_uri(child_path) + printable_path = child_path.decode(sys.getfilesystemencoding(), + 'ignore') + + if os.path.islink(child_path) and not self._follow_symlinks: + logger.debug('Ignoring symlink: %s', printable_path) continue - child = os.path.join(local_path, name) - logger.debug('child: %s', child) - uri = path.path_to_uri(child) - name = name.decode(sys.getfilesystemencoding(), 'ignore') - if not self._show_dotfiles and name.startswith(b'.'): + + if not self._is_in_basedir(os.path.realpath(child_path)): + logger.debug('Ignoring symlink to outside base dir: %s', + printable_path) continue - if self._follow_symlinks: - st = os.stat(child) - else: - st = os.lstat(child) - if stat.S_ISDIR(st.st_mode): - result.append(models.Ref.directory(name=name, uri=uri)) - elif stat.S_ISREG(st.st_mode) and self._is_audiofile(uri): - result.append(models.Ref.track(name=name, uri=uri)) - else: - logger.warn('Ignored file: %s', - child.decode(sys.getfilesystemencoding(), - 'ignore')) + + if not self._show_dotfiles and dir_entry.startswith(b'.'): continue + + if os.path.isdir(child_path): + result.append(models.Ref.directory(name=dir_entry, uri=uri)) + elif os.path.isfile(child_path): + if self._is_audiofile(uri): + result.append(models.Ref.track(name=dir_entry, uri=uri)) + else: + logger.debug('Ignoring non-audiofile: %s', printable_path) + result.sort(key=operator.attrgetter('name')) return result def lookup(self, uri): - logger.debug(u'looking up uri = %s', uri) + logger.debug('looking up uri = %s', uri) local_path = path.uri_to_path(uri) if not self._is_in_basedir(local_path): - logger.warn(u'Not in base_dir: %s', local_path) + logger.warn('Ignoring URI outside base dir: %s', local_path) return [] try: result = self._scanner.scan(uri) track = utils.convert_tags_to_track(result.tags).copy( uri=uri, length=result.duration) except exceptions.ScannerError as e: - logger.warning(u'Problem looking up %s: %s', uri, e) + logger.warning('Problem looking up %s: %s', uri, e) track = models.Track(uri=uri) if not track.name: filename = os.path.basename(local_path) @@ -91,7 +93,7 @@ class FilesLibraryProvider(backend.LibraryProvider): return [track] def _get_media_dirs(self, config): - for entry in config['files']['media_dir']: + for entry in config['files']['media_dirs']: media_dir = {} media_dir_split = entry.split('|', 1) local_path = path.expand_path(