From cfb32d9bc5450982a9a9a513cedf5ea553ecc8d2 Mon Sep 17 00:00:00 2001 From: David Logie Date: Mon, 3 May 2021 11:47:22 +0100 Subject: [PATCH 1/6] Add a `import.ignored_alias_types` option to ignore alias types. Sometimes a user may want to use an artist's locale-specific alias but *not* want to use their legal name, for example. --- beets/autotag/mb.py | 15 ++++++++++++--- beets/config_default.yaml | 1 + docs/changelog.rst | 2 ++ docs/reference/config.rst | 11 +++++++++++ 4 files changed, 26 insertions(+), 3 deletions(-) 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/docs/changelog.rst b/docs/changelog.rst index 11f04dee4..bd4413f41 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: 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 From aa09d2a9eb621ca2a428ecf20c6c4bf6843e4078 Mon Sep 17 00:00:00 2001 From: Callum Brown Date: Wed, 28 Sep 2022 21:04:33 +0100 Subject: [PATCH 2/6] AURA: document cors for local browser clients --- docs/plugins/aura.rst | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) 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 From 82d41446a22f7e55259aab469f9e7b99c50cc70f Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Sat, 1 Oct 2022 16:37:47 -0700 Subject: [PATCH 3/6] Use fsdecode for template substitution The idea in this PR is to converge on Python's `fsdecode` and `fsencode` for argument manipulation. This seems to match up with the Python standard library's assumptions: namely, on Windows, we use `fsdecode` to get back to Unicode strings: https://github.com/python/cpython/blob/54bbb5e3363ef3634f1fd7521ba3f3c42c81a331/Lib/subprocess.py#L561 So let's start by dropping this utility and going straight for `fsdecode` here to match. --- beets/util/__init__.py | 18 ------------------ beetsplug/convert.py | 6 +++--- beetsplug/duplicates.py | 5 +++-- 3 files changed, 6 insertions(+), 23 deletions(-) diff --git a/beets/util/__init__.py b/beets/util/__init__.py index 720ca311a..cb9c3f4ea 100644 --- a/beets/util/__init__.py +++ b/beets/util/__init__.py @@ -1092,21 +1092,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 95240dc39..17a18e358 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 @@ -214,8 +214,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) From 086bab55b1b04eb091fad8194fd641df76bff47a Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Sat, 1 Oct 2022 16:42:43 -0700 Subject: [PATCH 4/6] Standardize on Python's fsencode for arguments This can apparently never be `None`, as of Python 3.2. --- beets/util/__init__.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/beets/util/__init__.py b/beets/util/__init__.py index cb9c3f4ea..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] From 8baf3e302d7b2b9a4dfc0806cc112bb0ddb768cd Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Sat, 1 Oct 2022 17:13:57 -0700 Subject: [PATCH 5/6] Skip an unhelpful test on Windows On Windows, converting command-line arguments (hopefully!!!) only needs to deal with valid strings from the OS. So it is not really relevant to test with non-UTF-8, non-surrogate bytes. --- test/test_util.py | 1 + 1 file changed, 1 insertion(+) 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]) From 1a73a4550ae721391a55755ea9edf58f51c76de6 Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Sat, 1 Oct 2022 17:30:53 -0700 Subject: [PATCH 6/6] Changelog for #4507 --- docs/changelog.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 9df77d5e7..8ea1c0322 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -51,6 +51,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/``)