play plugin: $playlist marker for precise control where the playlist … (#4728)

…file is placed in the command

## Description

see included doc; placing the playlist filename at the end of command
just isn't working for all players

I have this in use with `mpv`

Co-authored-by: cvx35isl <cvx35isl@users.noreply.github.com>
Co-authored-by: J0J0 Todos <2733783+JOJ0@users.noreply.github.com>
This commit is contained in:
cvx35isl 2025-10-19 08:38:20 +02:00 committed by GitHub
parent 909b9aade4
commit 1275ccf8c1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 47 additions and 0 deletions

View file

@ -28,6 +28,11 @@ from beets.util import get_temp_filename
# If this is missing, they're placed at the end.
ARGS_MARKER = "$args"
# Indicate where the playlist file (with absolute path) should be inserted into
# the command string. If this is missing, its placed at the end, but before
# arguments.
PLS_MARKER = "$playlist"
def play(
command_str,
@ -132,8 +137,23 @@ class PlayPlugin(BeetsPlugin):
return
open_args = self._playlist_or_paths(paths)
open_args_str = [
p.decode("utf-8") for p in self._playlist_or_paths(paths)
]
command_str = self._command_str(opts.args)
if PLS_MARKER in command_str:
if not config["play"]["raw"]:
command_str = command_str.replace(
PLS_MARKER, "".join(open_args_str)
)
self._log.debug(
"command altered by PLS_MARKER to: {}", command_str
)
open_args = []
else:
command_str = command_str.replace(PLS_MARKER, " ")
# Check if the selection exceeds configured threshold. If True,
# cancel, otherwise proceed with play command.
if opts.yes or not self._exceeds_threshold(
@ -162,6 +182,7 @@ class PlayPlugin(BeetsPlugin):
return paths
else:
return [self._create_tmp_playlist(paths)]
return [shlex.quote(self._create_tmp_playlist(paths))]
def _exceeds_threshold(
self, selection, command_str, open_args, item_type="track"

View file

@ -10,6 +10,8 @@ Unreleased
New features:
- :doc:`plugins/ftintitle`: Added argument for custom feat. words in ftintitle.
- :doc: `/plugins/play`: Added `$playlist` marker to precisely edit the playlist
filepath into the command calling the player program.
Bug fixes:
@ -71,6 +73,8 @@ New features:
:bug:`3354`
- :doc:`plugins/discogs` Support for name variations and config options to
specify where the variations are written. :bug:`3354`
- :doc: `/plugins/play`: Added `$playlist` marker to precisely edit the playlist
filepath into the command calling the player program.
Bug fixes:

View file

@ -107,6 +107,15 @@ string, use ``$args`` to indicate where to insert them. For example:
indicates that you need to insert extra arguments before specifying the
playlist.
Some players require a different syntax. For example, with ``mpv`` the optional
``$playlist`` variable can be used to match the syntax of the ``--playlist``
option:
::
play:
command: mpv $args --playlist=$playlist
The ``--yes`` (or ``-y``) flag to the ``play`` command will skip the warning
message if you choose to play more items than the **warning_threshold** value
usually allows.

View file

@ -105,6 +105,19 @@ class PlayPluginTest(CleanupModulesMixin, PluginTestCase):
open_mock.assert_called_once_with([self.item.path], "echo")
def test_pls_marker(self, open_mock):
self.config["play"]["command"] = (
"echo --some params --playlist=$playlist --some-more params"
)
self.run_command("play", "nice")
open_mock.assert_called_once
commandstr = open_mock.call_args_list[0][0][1]
assert commandstr.startswith("echo --some params --playlist=")
assert commandstr.endswith(" --some-more params")
def test_not_found(self, open_mock):
self.run_command("play", "not found")