diff --git a/beets/config_default.yaml b/beets/config_default.yaml index 58136d56f..689ab44b1 100644 --- a/beets/config_default.yaml +++ b/beets/config_default.yaml @@ -32,6 +32,7 @@ replace: '\s+$': '' '^\s+': '' path_sep_replace: _ +asciify_paths: false art_filename: cover max_filename_length: 0 diff --git a/beets/library.py b/beets/library.py index 95a02c3b8..8496a844e 100644 --- a/beets/library.py +++ b/beets/library.py @@ -571,8 +571,13 @@ class Item(LibModel): subpath = unicodedata.normalize('NFD', subpath) else: subpath = unicodedata.normalize('NFC', subpath) + + if beets.config['asciify_paths']: + subpath = unidecode(subpath) + # Truncate components and remove forbidden characters. subpath = util.sanitize_path(subpath, self._db.replacements) + # Encode for the filesystem. if not fragment: subpath = bytestring_path(subpath) @@ -830,6 +835,8 @@ class Album(LibModel): filename_tmpl = Template(beets.config['art_filename'].get(unicode)) subpath = self.evaluate_template(filename_tmpl, True) + if beets.config['asciify_paths']: + subpath = unidecode(subpath) subpath = util.sanitize_path(subpath, replacements=self._db.replacements) subpath = bytestring_path(subpath) diff --git a/docs/changelog.rst b/docs/changelog.rst index 74ce6195b..bfc6da537 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -69,6 +69,9 @@ Little improvements and fixes: fingerprinting on import. Thanks to ddettrittus. * :doc:`/plugins/convert`: Add ``--format`` option to select the transoding command from the command-line. +* Add :ref:`asciify-paths` configuration option to replace non-ASCII + characters in paths. + 1.3.6 (May 10, 2014) diff --git a/docs/reference/config.rst b/docs/reference/config.rst index bf9fb6daa..b703d9c70 100644 --- a/docs/reference/config.rst +++ b/docs/reference/config.rst @@ -119,6 +119,22 @@ compatibility with Windows-influenced network filesystems like Samba). Trailing dots and trailing whitespace, which can cause problems on Windows clients, are also removed. +.. _asciify-paths: + +asciify_paths +~~~~~~~~~~~~~ + +Works like a specialized ``replace`` configuration. If set to ``yes``, +all non-ASCII characters in paths created by beets are converted to +their ASCII equivalents. For example, if your path template for +singletons is ``singletons/$title`` and the title of a track is "Café", +then the track will be saved as ``singletons/Cafe.mp3``. The changes +take place before applying the ``replace`` configuration. Uses the +mapping provided by the `unidecode module`_. Defaults to ``no``. + +.. _unidecode module: http://pypi.python.org/pypi/Unidecode + + .. _art-filename: art_filename diff --git a/docs/reference/pathformat.rst b/docs/reference/pathformat.rst index 005d4ed63..8efe54cd2 100644 --- a/docs/reference/pathformat.rst +++ b/docs/reference/pathformat.rst @@ -69,7 +69,8 @@ These functions are built in to beets: nothing if ``falsetext`` is left off). * ``%asciify{text}``: Convert non-ASCII characters to their ASCII equivalents. For example, "café" becomes "cafe". Uses the mapping provided by the - `unidecode module`_. + `unidecode module`_. See the :ref:`asciify-paths` configuration + option. * ``%aunique{identifiers,disambiguators}``: Provides a unique string to disambiguate similar albums in the database. See :ref:`aunique`, below. * ``%time{date_time,format}``: Return the date and time in any format accepted diff --git a/test/test_library.py b/test/test_library.py index c96d01c27..aac58b9e9 100644 --- a/test/test_library.py +++ b/test/test_library.py @@ -136,6 +136,10 @@ class DestinationTest(_common.TestCase): super(DestinationTest, self).tearDown() self.lib._connection().close() + # Reset config if it was changed in test cases + config.clear() + config.read(user=False, defaults=True) + def test_directory_works_with_trailing_slash(self): self.lib.directory = 'one/' self.lib.path_formats = [('default', 'two')] @@ -447,6 +451,14 @@ class DestinationTest(_common.TestCase): dest = self.i.destination(platform='linux2', fragment=True) self.assertEqual(dest, u'foo.caf\xe9') + def test_asciify_and_replace(self): + config['asciify_paths'] = True + self.lib.replacements = [(re.compile(u'"'), u'q')] + self.lib.directory = 'lib' + self.lib.path_formats = [('default', '$title')] + self.i.title = u'\u201c\u00f6\u2014\u00cf\u201d' + self.assertEqual(self.i.destination(), np('lib/qo--Iq')) + class ItemFormattedMappingTest(_common.LibTestCase): def test_formatted_item_value(self):