diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index e6a2e277f..3bd7e8c81 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -117,11 +117,20 @@ def _preferred_alias(aliases): # Only consider aliases that have locales set. aliases = [a for a in aliases if 'locale' in a] + # Get any ignored alias types and lower case them to prevent case issues + ignored_alias_types = config['import']['ignored_alias_types'].as_str_seq() + ignored_alias_types = [a.lower() for a in ignored_alias_types] + # Search configured locales in order. for locale in config['import']['languages'].as_str_seq(): - # Find matching primary aliases for this locale. - matches = [a for a in aliases - if a['locale'] == locale and 'primary' in a] + # Find matching primary aliases for this locale that are not + # being ignored + matches = [] + for a in aliases: + if a['locale'] == locale and 'primary' in a and \ + a.get('type', '').lower() not in ignored_alias_types: + matches.append(a) + # Skip to the next locale if we have no matches if not matches: continue diff --git a/beets/config_default.yaml b/beets/config_default.yaml index 6afb3e5a4..40598ae6a 100644 --- a/beets/config_default.yaml +++ b/beets/config_default.yaml @@ -33,6 +33,7 @@ import: duplicate_action: ask bell: no set_fields: {} + ignored_alias_types: [] clutter: ["Thumbs.DB", ".DS_Store"] ignore: [".*", "*~", "System Volume Information", "lost+found"] diff --git a/beets/util/__init__.py b/beets/util/__init__.py index 720ca311a..06e02ee08 100644 --- a/beets/util/__init__.py +++ b/beets/util/__init__.py @@ -17,7 +17,6 @@ import os import sys import errno -import locale import re import tempfile import shutil @@ -332,12 +331,7 @@ def arg_encoding(): """Get the encoding for command-line arguments (and other OS locale-sensitive strings). """ - try: - return locale.getdefaultlocale()[1] or 'utf-8' - except ValueError: - # Invalid locale environment variable setting. To avoid - # failing entirely for no good reason, assume UTF-8. - return 'utf-8' + return sys.getfilesystemencoding() def _fsencoding(): @@ -837,13 +831,14 @@ def cpu_count(): def convert_command_args(args): - """Convert command arguments to bytestrings on Python 2 and - surrogate-escaped strings on Python 3.""" + """Convert command arguments, which may either be `bytes` or `str` + objects, to uniformly surrogate-escaped strings. + """ assert isinstance(args, list) def convert(arg): if isinstance(arg, bytes): - arg = arg.decode(arg_encoding(), 'surrogateescape') + return os.fsdecode(arg) return arg return [convert(a) for a in args] @@ -1092,21 +1087,3 @@ def lazy_property(func): return value return wrapper - - -def decode_commandline_path(path): - """Prepare a path for substitution into commandline template. - - On Python 3, we need to construct the subprocess commands to invoke as a - Unicode string. On Unix, this is a little unfortunate---the OS is - expecting bytes---so we use surrogate escaping and decode with the - argument encoding, which is the same encoding that will then be - *reversed* to recover the same bytes before invoking the OS. On - Windows, we want to preserve the Unicode filename "as is." - """ - # On Python 3, the template is a Unicode string, which only supports - # substitution of Unicode variables. - if platform.system() == 'Windows': - return path.decode(_fsencoding()) - else: - return path.decode(arg_encoding(), 'surrogateescape') diff --git a/beetsplug/convert.py b/beetsplug/convert.py index b08b221a5..d427bc8b8 100644 --- a/beetsplug/convert.py +++ b/beetsplug/convert.py @@ -14,7 +14,7 @@ """Converts tracks or albums to external directory """ -from beets.util import par_map, decode_commandline_path, arg_encoding +from beets.util import par_map, arg_encoding import os import threading @@ -216,8 +216,8 @@ class ConvertPlugin(BeetsPlugin): self._log.info('Encoding {0}', util.displayable_path(source)) command = command.decode(arg_encoding(), 'surrogateescape') - source = decode_commandline_path(source) - dest = decode_commandline_path(dest) + source = os.fsdecode(source) + dest = os.fsdecode(dest) # Substitute $source and $dest in the argument list. args = shlex.split(command) diff --git a/beetsplug/duplicates.py b/beetsplug/duplicates.py index fdd5c1750..f655c61f2 100644 --- a/beetsplug/duplicates.py +++ b/beetsplug/duplicates.py @@ -16,11 +16,12 @@ """ import shlex +import os from beets.plugins import BeetsPlugin from beets.ui import decargs, print_, Subcommand, UserError from beets.util import command_output, displayable_path, subprocess, \ - bytestring_path, MoveOperation, decode_commandline_path + bytestring_path, MoveOperation from beets.library import Item, Album @@ -200,7 +201,7 @@ class DuplicatesPlugin(BeetsPlugin): output as flexattr on a key that is the name of the program, and return the key, checksum tuple. """ - args = [p.format(file=decode_commandline_path(item.path)) + args = [p.format(file=os.fsdecode(item.path)) for p in shlex.split(prog)] key = args[0] checksum = getattr(item, key, False) diff --git a/docs/changelog.rst b/docs/changelog.rst index 1de7b8e5f..a5ed9cb56 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -45,6 +45,8 @@ New features: :bug:`4379` :bug:`4387` * Add :ref:`%sunique{} ` template to disambiguate between singletons. :bug:`4438` +* Add a new ``import.ignored_alias_types`` config option to allow for + specific alias types to be skipped over when importing items/albums. Bug fixes: @@ -53,6 +55,10 @@ Bug fixes: * :doc:`/plugins/replaygain`: Avoid a crash when errors occur in the analysis backend. :bug:`4506` +* We now use Python's defaults for command-line argument encoding, which + should reduce the chance for errors and "file not found" failures when + invoking other command-line tools, especially on Windows. + :bug:`4507` * We now respect the Spotify API's rate limiting, which avoids crashing when the API reports code 429 (too many requests). :bug:`4370` * Fix implicit paths OR queries (e.g. ``beet list /path/ , /other-path/``) diff --git a/docs/plugins/aura.rst b/docs/plugins/aura.rst index 83c186249..eadb9ef38 100644 --- a/docs/plugins/aura.rst +++ b/docs/plugins/aura.rst @@ -49,6 +49,7 @@ then see :ref:`aura-external-server`. AURA is designed to separate the client and server functionality. This plugin provides the server but not the client, so unless you like looking at JSON you will need a separate client. Currently the only client is `AURA Web Client`_. +In order to use a local browser client with ``file:///`` see :ref:`aura-cors`. By default the API is served under http://127.0.0.1:8337/aura/. For example information about the track with an id of 3 can be obtained at @@ -97,11 +98,18 @@ For example:: - http://www.example.com - https://aura.example.org -Alternatively you can set it to ``'*'`` to enable access from all origins. +In order to use the plugin with a local browser client accessed using +``file:///`` you must inclue ``'null'`` in the list of allowed origins +(including quote marks):: + + aura: + cors: + - 'null' + +Alternatively you use ``'*'`` to enable access from all origins. Note that there are security implications if you set the origin to ``'*'``, so please research this before using it. Note the use of quote marks when -allowing all origins. Quote marks are also required when the origin is -``null``, for example when using ``file:///``. +allowing all origins. If the server is behind a proxy that uses credentials, you might want to set the ``cors_supports_credentials`` configuration option to true to let diff --git a/docs/reference/config.rst b/docs/reference/config.rst index 99f916e88..afabe1aa0 100644 --- a/docs/reference/config.rst +++ b/docs/reference/config.rst @@ -652,6 +652,17 @@ MusicBrainz. You can use a space-separated list of language abbreviations, like ``en jp es``, to specify a preference order. Defaults to an empty list, meaning that no language is preferred. +.. _ignored_alias_types: + +ignored_alias_types +~~~~~~~~~~~~~~~~~~~ + +A list of alias types to be ignored when importing new items. + +See the `MusicBrainz Documentation` for more information on aliases. + +.._MusicBrainz Documentation: https://musicbrainz.org/doc/Aliases + .. _detail: detail diff --git a/test/test_util.py b/test/test_util.py index fcaf9f5ce..14ac7f2b2 100644 --- a/test/test_util.py +++ b/test/test_util.py @@ -101,6 +101,7 @@ class UtilTest(unittest.TestCase): ]) self.assertEqual(p, 'foo/_/bar') + @unittest.skipIf(sys.platform == 'win32', 'win32') def test_convert_command_args_keeps_undecodeable_bytes(self): arg = b'\x82' # non-ascii bytes cmd_args = util.convert_command_args([arg])