Skip to content

Commit ea703e3

Browse files
bastimeyergravyboat
authored andcommitted
cli: optional player-args input variable
- Make the player input variable in the `--player-args` parameter optional. If it's missing, append it to the parameter value. - Rename it from `{filename}` to `{playerinput}`, to reduce confusion and add `{filename}` back as a secondary fallback variable. - Update parameter docs accordingly and add+fix tests
1 parent 2c8f46b commit ea703e3

File tree

5 files changed

+81
-28
lines changed

5 files changed

+81
-28
lines changed

src/streamlink_cli/argparser.py

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
boolean, comma_list, comma_list_filter, filesize, keyvalue, num
1010
)
1111
from streamlink.utils.times import hours_minutes_seconds
12-
from streamlink_cli.constants import (DEFAULT_PLAYER_ARGUMENTS, DEFAULT_STREAM_METADATA, STREAM_PASSTHROUGH, SUPPORTED_PLAYERS)
12+
from streamlink_cli.constants import (
13+
DEFAULT_STREAM_METADATA, PLAYER_ARGS_INPUT_DEFAULT, PLAYER_ARGS_INPUT_FALLBACK, STREAM_PASSTHROUGH, SUPPORTED_PLAYERS
14+
)
1315
from streamlink_cli.utils import find_default_player
1416

1517
_printable_re = re.compile(r"[{0}]".format(printable))
@@ -310,33 +312,36 @@ def build_parser():
310312
player.add_argument(
311313
"-a", "--player-args",
312314
metavar="ARGUMENTS",
313-
default=DEFAULT_PLAYER_ARGUMENTS,
314-
help="""
315+
default="",
316+
help=f"""
315317
This option allows you to customize the default arguments which are put
316318
together with the value of --player to create a command to execute.
317-
Unlike the --player parameter, custom player arguments will not be logged.
318319
319-
This value can contain formatting variables surrounded by curly braces,
320+
It's usually enough to only use --player instead of this unless you need
321+
to add arguments after the player's input argument or if you don't want
322+
any of the player arguments to be logged.
323+
324+
The value can contain formatting variables surrounded by curly braces,
320325
{{ and }}. If you need to include a brace character, it can be escaped
321326
by doubling, e.g. {{{{ and }}}}.
322327
323328
Formatting variables available:
324329
325-
{{filename}}
326-
This is the filename that the player will use. It's usually "-"
327-
(stdin), but can also be a URL or a file depending on the options
328-
used.
329-
330-
It's usually enough to use --player instead of this unless you need to
331-
add arguments after the filename.
330+
{{{PLAYER_ARGS_INPUT_DEFAULT}}}
331+
This is the input that the player will use. For standard input (stdin),
332+
it is ``-``, but it can also be a URL, depending on the options used.
332333
333-
Default is "{0}".
334+
{{{PLAYER_ARGS_INPUT_FALLBACK}}}
335+
The old fallback variable name with the same functionality.
334336
335337
Example:
336338
337-
%(prog)s -p vlc -a "--play-and-exit {{filename}}" <url> [stream]
339+
%(prog)s -p vlc -a "--play-and-exit {{{PLAYER_ARGS_INPUT_DEFAULT}}}" <url> [stream]
338340
339-
""".format(DEFAULT_PLAYER_ARGUMENTS)
341+
Note: When neither of the variables are found, ``{{{PLAYER_ARGS_INPUT_DEFAULT}}}``
342+
will be appended to the whole parameter value, to ensure that the player
343+
always receives an input argument.
344+
"""
340345
)
341346
player.add_argument(
342347
"-v", "--verbose-player",

src/streamlink_cli/constants.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
from streamlink_cli.compat import is_win32
44

5-
DEFAULT_PLAYER_ARGUMENTS = "{filename}"
5+
PLAYER_ARGS_INPUT_DEFAULT = "playerinput"
6+
PLAYER_ARGS_INPUT_FALLBACK = "filename"
7+
68
DEFAULT_STREAM_METADATA = {
79
"title": "Unknown Title",
810
"author": "Unknown Author",
@@ -34,6 +36,7 @@
3436
STREAM_PASSTHROUGH = ["hls", "http", "rtmp"]
3537

3638
__all__ = [
37-
"CONFIG_FILES", "DEFAULT_PLAYER_ARGUMENTS",
38-
"PLUGINS_DIR", "STREAM_SYNONYMS", "STREAM_PASSTHROUGH"
39+
"PLAYER_ARGS_INPUT_DEFAULT", "PLAYER_ARGS_INPUT_FALLBACK",
40+
"DEFAULT_STREAM_METADATA", "SUPPORTED_PLAYERS",
41+
"CONFIG_FILES", "PLUGINS_DIR", "STREAM_SYNONYMS", "STREAM_PASSTHROUGH"
3942
]

src/streamlink_cli/output.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import logging
22
import os
3+
import re
34
import shlex
45
import subprocess
56
import sys
67
from time import sleep
78

89
from streamlink_cli.compat import is_win32, stdout
9-
from streamlink_cli.constants import DEFAULT_PLAYER_ARGUMENTS, SUPPORTED_PLAYERS
10+
from streamlink_cli.constants import PLAYER_ARGS_INPUT_DEFAULT, PLAYER_ARGS_INPUT_FALLBACK, SUPPORTED_PLAYERS
1011
from streamlink_cli.utils import ignored
1112

1213
if is_win32:
@@ -77,7 +78,12 @@ def _write(self, data):
7778
class PlayerOutput(Output):
7879
PLAYER_TERMINATE_TIMEOUT = 10.0
7980

80-
def __init__(self, cmd, args=DEFAULT_PLAYER_ARGUMENTS, filename=None, quiet=True, kill=True,
81+
_re_player_args_input = re.compile("|".join(map(
82+
lambda const: re.escape(f"{{{const}}}"),
83+
[PLAYER_ARGS_INPUT_DEFAULT, PLAYER_ARGS_INPUT_FALLBACK]
84+
)))
85+
86+
def __init__(self, cmd, args="", filename=None, quiet=True, kill=True,
8187
call=False, http=None, namedpipe=None, record=None, title=None):
8288
super().__init__()
8389
self.cmd = cmd
@@ -106,6 +112,9 @@ def __init__(self, cmd, args=DEFAULT_PLAYER_ARGUMENTS, filename=None, quiet=True
106112
self.stdout = sys.stdout
107113
self.stderr = sys.stderr
108114

115+
if not self._re_player_args_input.search(self.args):
116+
self.args += f"{' ' if self.args else ''}{{{PLAYER_ARGS_INPUT_DEFAULT}}}"
117+
109118
@property
110119
def running(self):
111120
sleep(0.5)
@@ -201,7 +210,7 @@ def _create_arguments(self):
201210
self.title = self.title.replace('"', '')
202211
filename = filename[:-1] + '\\' + self.title + filename[-1]
203212

204-
args = self.args.format(filename=filename)
213+
args = self.args.format(**{PLAYER_ARGS_INPUT_DEFAULT: filename, PLAYER_ARGS_INPUT_FALLBACK: filename})
205214
cmd = self.cmd
206215

207216
# player command

tests/test_cli_main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ def test_create_output_no_file_output_options(self):
139139
args.record_and_pipe = None
140140
args.title = None
141141
args.player = "mpv"
142+
args.player_args = ""
142143
self.assertIsInstance(create_output(FakePlugin), PlayerOutput)
143144

144145
def test_create_output_file_output(self):
@@ -182,6 +183,7 @@ def test_create_output_record(self):
182183
args.record_and_pipe = None
183184
args.title = None
184185
args.player = "mpv"
186+
args.player_args = ""
185187
args.player_fifo = None
186188
self.assertIsInstance(create_output(FakePlugin), PlayerOutput)
187189
finally:

tests/test_cli_playerout.py

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from unittest.mock import ANY, patch
1+
from unittest.mock import ANY, Mock, patch
22

33
from streamlink_cli.output import PlayerOutput
44
from tests import posix_only, windows_only
@@ -7,27 +7,30 @@
77

88

99
@posix_only
10-
@patch('subprocess.Popen')
10+
@patch("streamlink_cli.output.sleep", Mock())
11+
@patch("subprocess.Popen")
1112
def test_output_mpv_unicode_title_posix(popen):
1213
po = PlayerOutput("mpv", title=UNICODE_TITLE)
1314
popen().poll.side_effect = lambda: None
1415
po.open()
15-
popen.assert_called_with(['mpv', f'--title={UNICODE_TITLE}', '-'],
16+
popen.assert_called_with(["mpv", f"--title={UNICODE_TITLE}", "-"],
1617
bufsize=ANY, stderr=ANY, stdout=ANY, stdin=ANY)
1718

1819

1920
@posix_only
20-
@patch('subprocess.Popen')
21+
@patch("streamlink_cli.output.sleep", Mock())
22+
@patch("subprocess.Popen")
2123
def test_output_vlc_unicode_title_posix(popen):
2224
po = PlayerOutput("vlc", title=UNICODE_TITLE)
2325
popen().poll.side_effect = lambda: None
2426
po.open()
25-
popen.assert_called_with(['vlc', '--input-title-format', UNICODE_TITLE, '-'],
27+
popen.assert_called_with(["vlc", "--input-title-format", UNICODE_TITLE, "-"],
2628
bufsize=ANY, stderr=ANY, stdout=ANY, stdin=ANY)
2729

2830

2931
@windows_only
30-
@patch('subprocess.Popen')
32+
@patch("streamlink_cli.output.sleep", Mock())
33+
@patch("subprocess.Popen")
3134
def test_output_mpv_unicode_title_windows_py3(popen):
3235
po = PlayerOutput("mpv.exe", title=UNICODE_TITLE)
3336
popen().poll.side_effect = lambda: None
@@ -37,10 +40,41 @@ def test_output_mpv_unicode_title_windows_py3(popen):
3740

3841

3942
@windows_only
40-
@patch('subprocess.Popen')
43+
@patch("streamlink_cli.output.sleep", Mock())
44+
@patch("subprocess.Popen")
4145
def test_output_vlc_unicode_title_windows_py3(popen):
4246
po = PlayerOutput("vlc.exe", title=UNICODE_TITLE)
4347
popen().poll.side_effect = lambda: None
4448
po.open()
4549
popen.assert_called_with(f"vlc.exe --input-title-format \"{UNICODE_TITLE}\" -",
4650
bufsize=ANY, stderr=ANY, stdout=ANY, stdin=ANY)
51+
52+
53+
@posix_only
54+
def test_output_args_posix():
55+
po_none = PlayerOutput("foo")
56+
assert po_none._create_arguments() == ["foo", "-"]
57+
58+
po_implicit = PlayerOutput("foo", args="--bar")
59+
assert po_implicit._create_arguments() == ["foo", "--bar", "-"]
60+
61+
po_explicit = PlayerOutput("foo", args="--bar {playerinput}")
62+
assert po_explicit._create_arguments() == ["foo", "--bar", "-"]
63+
64+
po_fallback = PlayerOutput("foo", args="--bar {filename}")
65+
assert po_fallback._create_arguments() == ["foo", "--bar", "-"]
66+
67+
68+
@windows_only
69+
def test_output_args_windows():
70+
po_none = PlayerOutput("foo")
71+
assert po_none._create_arguments() == "foo -"
72+
73+
po_implicit = PlayerOutput("foo", args="--bar")
74+
assert po_implicit._create_arguments() == "foo --bar -"
75+
76+
po_explicit = PlayerOutput("foo", args="--bar {playerinput}")
77+
assert po_explicit._create_arguments() == "foo --bar -"
78+
79+
po_fallback = PlayerOutput("foo", args="--bar {filename}")
80+
assert po_fallback._create_arguments() == "foo --bar -"

0 commit comments

Comments
 (0)