diff --git a/beets/ui/commands.py b/beets/ui/commands.py index 2a37a113d..f3386cf71 100644 --- a/beets/ui/commands.py +++ b/beets/ui/commands.py @@ -1485,7 +1485,7 @@ def config_edit(): try: if not os.path.isfile(path): open(path, 'w+').close() - util.interactive_open(path, editor) + util.interactive_open([path], editor) except OSError as exc: message = "Could not edit configuration: {0}".format(exc) if not editor: diff --git a/beets/util/__init__.py b/beets/util/__init__.py index 61a95baa2..158a6c8e1 100644 --- a/beets/util/__init__.py +++ b/beets/util/__init__.py @@ -735,8 +735,8 @@ def open_anything(): return base_cmd -def interactive_open(target, command=None): - """Open the file `target` by `exec`ing a new command. (The new +def interactive_open(targets, command=None): + """Open the files in `targets` by `exec`ing a new command. (The new program takes over, and Python execution ends: this does not fork a subprocess.) @@ -757,5 +757,6 @@ def interactive_open(target, command=None): base_cmd = open_anything() command = [base_cmd, base_cmd] - command.append(target) + command += targets + return os.execlp(*command) diff --git a/beetsplug/play.py b/beetsplug/play.py index e6611ad3f..038bfd42d 100644 --- a/beetsplug/play.py +++ b/beetsplug/play.py @@ -39,6 +39,7 @@ class PlayPlugin(BeetsPlugin): 'command': None, 'use_folders': False, 'relative_to': None, + 'raw': False, }) def commands(self): @@ -62,6 +63,7 @@ class PlayPlugin(BeetsPlugin): command_str = config['play']['command'].get() use_folders = config['play']['use_folders'].get(bool) relative_to = config['play']['relative_to'].get() + raw = config['play']['raw'].get(bool) if relative_to: relative_to = util.normpath(relative_to) @@ -91,6 +93,8 @@ class PlayPlugin(BeetsPlugin): else: selection = lib.items(ui.decargs(args)) paths = [item.path for item in selection] + if relative_to: + paths = [relpath(path, relative_to) for path in paths] item_type = 'track' item_type += 's' if len(selection) > 1 else '' @@ -111,22 +115,29 @@ class PlayPlugin(BeetsPlugin): if ui.input_options(('Continue', 'Abort')) == 'a': return - # Create temporary m3u file to hold our playlist. - m3u = NamedTemporaryFile('w', suffix='.m3u', delete=False) - for item in paths: - if relative_to: - m3u.write(relpath(item, relative_to) + b'\n') - else: - m3u.write(item + b'\n') - m3u.close() - ui.print_(u'Playing {0} {1}.'.format(len(selection), item_type)) + if raw: + open_args = paths + else: + open_args = self._create_tmp_playlist(paths) - self._log.debug('executing command: {} {}', command_str, m3u.name) + self._log.debug('executing command: {} {}', command_str, + b'"' + b' '.join(open_args) + b'"') try: - util.interactive_open(m3u.name, command_str) + util.interactive_open(open_args, command_str) except OSError as exc: raise ui.UserError("Could not play the music playlist: " "{0}".format(exc)) finally: - util.remove(m3u.name) + if not raw: + self._log.debug('Removing temporary playlist: {}', + open_args[0]) + util.remove(open_args[0]) + + def _create_tmp_playlist(self, paths_list): + # Create temporary m3u file to hold our playlist. + m3u = NamedTemporaryFile('w', suffix='.m3u', delete=False) + for item in paths_list: + m3u.write(item + b'\n') + m3u.close() + return [m3u.name] diff --git a/docs/plugins/play.rst b/docs/plugins/play.rst index 08172c7c9..9af886c13 100644 --- a/docs/plugins/play.rst +++ b/docs/plugins/play.rst @@ -44,6 +44,9 @@ configuration file. The available options are: paths to each track on the matched albums. Enable this option to store paths to folders instead. Default: ``no``. +- **raw**: Instead of creating a temporary m3u playlist and then opening it, + simply call the command with the paths returned by the query as arguments. + Default: ``no``. Optional Arguments ------------------ diff --git a/test/test_util.py b/test/test_util.py index 464c7e678..324a4d589 100644 --- a/test/test_util.py +++ b/test/test_util.py @@ -43,11 +43,11 @@ class UtilTest(unittest.TestCase): @patch('beets.util.open_anything') def test_interactive_open(self, mock_open, mock_execlp): mock_open.return_value = 'tagada' - util.interactive_open('foo') + util.interactive_open(['foo']) mock_execlp.assert_called_once_with('tagada', 'tagada', 'foo') mock_execlp.reset_mock() - util.interactive_open('foo', 'bar') + util.interactive_open(['foo'], 'bar') mock_execlp.assert_called_once_with('bar', 'bar', 'foo') def test_sanitize_unix_replaces_leading_dot(self):