We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Unable to use --player-fifo with MPV
Streamlink 2.0.0
mpv 0.33.0-76-g93066ff12f Copyright © 2000-2020 mpv/MPlayer/mplayer2 projects

```
streamlink --player-fifo https://www.twitch.tv/channelName best
[cli][info] Found matching plugin twitch for URL https://www.twitch.tv/channelName 
[cli][info] Available streams: audio_only, 160p (worst), 360p, 480p, 720p, 720p60, 1080p60 (best)
[cli][info] Opening stream: 1080p60 (hls)
[cli][info] Creating pipe streamlinkpipe-1140
[cli][info] Starting player: "c:\Programs\MPV\mpv.exe"
error: Failed to start player: "c:\Programs\MPV\mpv.exe" (Process exited prematurely)
[cli][info] Closing currently open stream...
```

Works normally without FIFO

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/streamlink_cli/output.py`
Content:
```
1 import logging
2 import os
3 import re
4 import shlex
5 import subprocess
6 import sys
7 from time import sleep
8 
9 from streamlink_cli.compat import is_win32, stdout
10 from streamlink_cli.constants import PLAYER_ARGS_INPUT_DEFAULT, PLAYER_ARGS_INPUT_FALLBACK, SUPPORTED_PLAYERS
11 from streamlink_cli.utils import ignored
12 
13 if is_win32:
14     import msvcrt
15 
16 log = logging.getLogger("streamlink.cli.output")
17 
18 
19 class Output:
20     def __init__(self):
21         self.opened = False
22 
23     def open(self):
24         self._open()
25         self.opened = True
26 
27     def close(self):
28         if self.opened:
29             self._close()
30 
31         self.opened = False
32 
33     def write(self, data):
34         if not self.opened:
35             raise OSError("Output is not opened")
36 
37         return self._write(data)
38 
39     def _open(self):
40         pass
41 
42     def _close(self):
43         pass
44 
45     def _write(self, data):
46         pass
47 
48 
49 class FileOutput(Output):
50     def __init__(self, filename=None, fd=None, record=None):
51         super().__init__()
52         self.filename = filename
53         self.fd = fd
54         self.record = record
55 
56     def _open(self):
57         if self.filename:
58             self.fd = open(self.filename, "wb")
59 
60         if self.record:
61             self.record.open()
62 
63         if is_win32:
64             msvcrt.setmode(self.fd.fileno(), os.O_BINARY)
65 
66     def _close(self):
67         if self.fd is not stdout:
68             self.fd.close()
69         if self.record:
70             self.record.close()
71 
72     def _write(self, data):
73         self.fd.write(data)
74         if self.record:
75             self.record.write(data)
76 
77 
78 class PlayerOutput(Output):
79     PLAYER_TERMINATE_TIMEOUT = 10.0
80 
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,
87                  call=False, http=None, namedpipe=None, record=None, title=None):
88         super().__init__()
89         self.cmd = cmd
90         self.args = args
91         self.kill = kill
92         self.call = call
93         self.quiet = quiet
94 
95         self.filename = filename
96         self.namedpipe = namedpipe
97         self.http = http
98         self.title = title
99         self.player = None
100         self.player_name = self.supported_player(self.cmd)
101         self.record = record
102 
103         if self.namedpipe or self.filename or self.http:
104             self.stdin = sys.stdin
105         else:
106             self.stdin = subprocess.PIPE
107 
108         if self.quiet:
109             self.stdout = open(os.devnull, "w")
110             self.stderr = open(os.devnull, "w")
111         else:
112             self.stdout = sys.stdout
113             self.stderr = sys.stderr
114 
115         if not self._re_player_args_input.search(self.args):
116             self.args += f"{' ' if self.args else ''}{{{PLAYER_ARGS_INPUT_DEFAULT}}}"
117 
118     @property
119     def running(self):
120         sleep(0.5)
121         return self.player.poll() is None
122 
123     @classmethod
124     def supported_player(cls, cmd):
125         """
126         Check if the current player supports adding a title
127 
128         :param cmd: command to test
129         :return: name of the player|None
130         """
131         if not is_win32:
132             # under a POSIX system use shlex to find the actual command
133             # under windows this is not an issue because executables end in .exe
134             cmd = shlex.split(cmd)[0]
135 
136         cmd = os.path.basename(cmd.lower())
137         for player, possiblecmds in SUPPORTED_PLAYERS.items():
138             for possiblecmd in possiblecmds:
139                 if cmd.startswith(possiblecmd):
140                     return player
141 
142     @classmethod
143     def _mpv_title_escape(cls, title_string):
144         # mpv has a "disable property-expansion" token which must be handled
145         # in order to accurately represent $$ in title
146         if r'\$>' in title_string:
147             processed_title = ""
148             double_dollars = True
149             i = dollars = 0
150             while i < len(title_string):
151                 if double_dollars:
152                     if title_string[i] == "\\":
153                         if title_string[i + 1] == "$":
154                             processed_title += "$"
155                             dollars += 1
156                             i += 1
157                             if title_string[i + 1] == ">" and dollars % 2 == 1:
158                                 double_dollars = False
159                                 processed_title += ">"
160                                 i += 1
161                         else:
162                             processed_title += "\\"
163                     elif title_string[i] == "$":
164                         processed_title += "$$"
165                     else:
166                         dollars = 0
167                         processed_title += title_string[i]
168                 else:
169                     if title_string[i:i + 2] == "\\$":
170                         processed_title += "$"
171                         i += 1
172                     else:
173                         processed_title += title_string[i]
174                 i += 1
175             return processed_title
176         else:
177             # not possible for property-expansion to be disabled, happy days
178             return title_string.replace("$", "$$").replace(r'\$$', "$")
179 
180     def _create_arguments(self):
181         if self.namedpipe:
182             filename = self.namedpipe.path
183         elif self.filename:
184             filename = self.filename
185         elif self.http:
186             filename = self.http.url
187         else:
188             filename = "-"
189         extra_args = []
190 
191         if self.title is not None:
192             # vlc
193             if self.player_name == "vlc":
194                 # see https://wiki.videolan.org/Documentation:Format_String/, allow escaping with \$
195                 self.title = self.title.replace("$", "$$").replace(r'\$$', "$")
196                 extra_args.extend(["--input-title-format", self.title])
197 
198             # mpv
199             if self.player_name == "mpv":
200                 # see https://mpv.io/manual/stable/#property-expansion, allow escaping with \$, respect mpv's $>
201                 self.title = self._mpv_title_escape(self.title)
202                 extra_args.append(f"--force-media-title={self.title}")
203 
204             # potplayer
205             if self.player_name == "potplayer":
206                 if filename != "-":
207                     # PotPlayer - About - Command Line
208                     # You can specify titles for URLs by separating them with a backslash (\) at the end of URLs.
209                     # eg. "http://...\title of this url"
210                     self.title = self.title.replace('"', '')
211                     filename = filename[:-1] + '\\' + self.title + filename[-1]
212 
213         args = self.args.format(**{PLAYER_ARGS_INPUT_DEFAULT: filename, PLAYER_ARGS_INPUT_FALLBACK: filename})
214         cmd = self.cmd
215 
216         # player command
217         if is_win32:
218             eargs = subprocess.list2cmdline(extra_args)
219             # do not insert and extra " " when there are no extra_args
220             return " ".join([cmd] + ([eargs] if eargs else []) + [args])
221         return shlex.split(cmd) + extra_args + shlex.split(args)
222 
223     def _open(self):
224         try:
225             if self.record:
226                 self.record.open()
227             if self.call and self.filename:
228                 self._open_call()
229             else:
230                 self._open_subprocess()
231         finally:
232             if self.quiet:
233                 # Output streams no longer needed in parent process
234                 self.stdout.close()
235                 self.stderr.close()
236 
237     def _open_call(self):
238         args = self._create_arguments()
239         if is_win32:
240             fargs = args
241         else:
242             fargs = subprocess.list2cmdline(args)
243         log.debug(f"Calling: {fargs}")
244 
245         subprocess.call(args,
246                         stdout=self.stdout,
247                         stderr=self.stderr)
248 
249     def _open_subprocess(self):
250         # Force bufsize=0 on all Python versions to avoid writing the
251         # unflushed buffer when closing a broken input pipe
252         args = self._create_arguments()
253         if is_win32:
254             fargs = args
255         else:
256             fargs = subprocess.list2cmdline(args)
257         log.debug(f"Opening subprocess: {fargs}")
258 
259         self.player = subprocess.Popen(args,
260                                        stdin=self.stdin, bufsize=0,
261                                        stdout=self.stdout,
262                                        stderr=self.stderr)
263         # Wait 0.5 seconds to see if program exited prematurely
264         if not self.running:
265             raise OSError("Process exited prematurely")
266 
267         if self.namedpipe:
268             self.namedpipe.open("wb")
269         elif self.http:
270             self.http.open()
271 
272     def _close(self):
273         # Close input to the player first to signal the end of the
274         # stream and allow the player to terminate of its own accord
275         if self.namedpipe:
276             self.namedpipe.close()
277         elif self.http:
278             self.http.close()
279         elif not self.filename:
280             self.player.stdin.close()
281 
282         if self.record:
283             self.record.close()
284 
285         if self.kill:
286             with ignored(Exception):
287                 self.player.terminate()
288                 if not is_win32:
289                     t, timeout = 0.0, self.PLAYER_TERMINATE_TIMEOUT
290                     while self.player.poll() is None and t < timeout:
291                         sleep(0.5)
292                         t += 0.5
293 
294                     if not self.player.returncode:
295                         self.player.kill()
296         self.player.wait()
297 
298     def _write(self, data):
299         if self.record:
300             self.record.write(data)
301 
302         if self.namedpipe:
303             self.namedpipe.write(data)
304         elif self.http:
305             self.http.write(data)
306         else:
307             self.player.stdin.write(data)
308 
309 
310 __all__ = ["PlayerOutput", "FileOutput"]
311
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/streamlink_cli/output.py b/src/streamlink_cli/output.py
--- a/src/streamlink_cli/output.py
+++ b/src/streamlink_cli/output.py
@@ -180,6 +180,11 @@
     def _create_arguments(self):
         if self.namedpipe:
             filename = self.namedpipe.path
+            if is_win32:
+                if self.player_name == "vlc":
+                    filename = f"stream://\\{filename}"
+                elif self.player_name == "mpv":
+                    filename = f"file://{filename}"
         elif self.filename:
             filename = self.filename
         elif self.http:

{"golden_diff": "diff --git a/src/streamlink_cli/output.py b/src/streamlink_cli/output.py\n--- a/src/streamlink_cli/output.py\n+++ b/src/streamlink_cli/output.py\n@@ -180,6 +180,11 @@\n     def _create_arguments(self):\n         if self.namedpipe:\n             filename = self.namedpipe.path\n+            if is_win32:\n+                if self.player_name == \"vlc\":\n+                    filename = f\"stream://\\\\{filename}\"\n+                elif self.player_name == \"mpv\":\n+                    filename = f\"file://{filename}\"\n         elif self.filename:\n             filename = self.filename\n         elif self.http:\n", "issue": "Unable to use --player-fifo with MPV\nStreamlink 2.0.0\r\nmpv 0.33.0-76-g93066ff12f Copyright \u00a9 2000-2020 mpv/MPlayer/mplayer2 projects\r\n\r\n```\r\nstreamlink --player-fifo https://www.twitch.tv/channelName best\r\n[cli][info] Found matching plugin twitch for URL https://www.twitch.tv/channelName \r\n[cli][info] Available streams: audio_only, 160p (worst), 360p, 480p, 720p, 720p60, 1080p60 (best)\r\n[cli][info] Opening stream: 1080p60 (hls)\r\n[cli][info] Creating pipe streamlinkpipe-1140\r\n[cli][info] Starting player: \"c:\\Programs\\MPV\\mpv.exe\"\r\nerror: Failed to start player: \"c:\\Programs\\MPV\\mpv.exe\" (Process exited prematurely)\r\n[cli][info] Closing currently open stream...\r\n```\r\n\r\nWorks normally without FIFO\n", "before_files": [{"content": "import logging\nimport os\nimport re\nimport shlex\nimport subprocess\nimport sys\nfrom time import sleep\n\nfrom streamlink_cli.compat import is_win32, stdout\nfrom streamlink_cli.constants import PLAYER_ARGS_INPUT_DEFAULT, PLAYER_ARGS_INPUT_FALLBACK, SUPPORTED_PLAYERS\nfrom streamlink_cli.utils import ignored\n\nif is_win32:\n    import msvcrt\n\nlog = logging.getLogger(\"streamlink.cli.output\")\n\n\nclass Output:\n    def __init__(self):\n        self.opened = False\n\n    def open(self):\n        self._open()\n        self.opened = True\n\n    def close(self):\n        if self.opened:\n            self._close()\n\n        self.opened = False\n\n    def write(self, data):\n        if not self.opened:\n            raise OSError(\"Output is not opened\")\n\n        return self._write(data)\n\n    def _open(self):\n        pass\n\n    def _close(self):\n        pass\n\n    def _write(self, data):\n        pass\n\n\nclass FileOutput(Output):\n    def __init__(self, filename=None, fd=None, record=None):\n        super().__init__()\n        self.filename = filename\n        self.fd = fd\n        self.record = record\n\n    def _open(self):\n        if self.filename:\n            self.fd = open(self.filename, \"wb\")\n\n        if self.record:\n            self.record.open()\n\n        if is_win32:\n            msvcrt.setmode(self.fd.fileno(), os.O_BINARY)\n\n    def _close(self):\n        if self.fd is not stdout:\n            self.fd.close()\n        if self.record:\n            self.record.close()\n\n    def _write(self, data):\n        self.fd.write(data)\n        if self.record:\n            self.record.write(data)\n\n\nclass PlayerOutput(Output):\n    PLAYER_TERMINATE_TIMEOUT = 10.0\n\n    _re_player_args_input = re.compile(\"|\".join(map(\n        lambda const: re.escape(f\"{{{const}}}\"),\n        [PLAYER_ARGS_INPUT_DEFAULT, PLAYER_ARGS_INPUT_FALLBACK]\n    )))\n\n    def __init__(self, cmd, args=\"\", filename=None, quiet=True, kill=True,\n                 call=False, http=None, namedpipe=None, record=None, title=None):\n        super().__init__()\n        self.cmd = cmd\n        self.args = args\n        self.kill = kill\n        self.call = call\n        self.quiet = quiet\n\n        self.filename = filename\n        self.namedpipe = namedpipe\n        self.http = http\n        self.title = title\n        self.player = None\n        self.player_name = self.supported_player(self.cmd)\n        self.record = record\n\n        if self.namedpipe or self.filename or self.http:\n            self.stdin = sys.stdin\n        else:\n            self.stdin = subprocess.PIPE\n\n        if self.quiet:\n            self.stdout = open(os.devnull, \"w\")\n            self.stderr = open(os.devnull, \"w\")\n        else:\n            self.stdout = sys.stdout\n            self.stderr = sys.stderr\n\n        if not self._re_player_args_input.search(self.args):\n            self.args += f\"{' ' if self.args else ''}{{{PLAYER_ARGS_INPUT_DEFAULT}}}\"\n\n    @property\n    def running(self):\n        sleep(0.5)\n        return self.player.poll() is None\n\n    @classmethod\n    def supported_player(cls, cmd):\n        \"\"\"\n        Check if the current player supports adding a title\n\n        :param cmd: command to test\n        :return: name of the player|None\n        \"\"\"\n        if not is_win32:\n            # under a POSIX system use shlex to find the actual command\n            # under windows this is not an issue because executables end in .exe\n            cmd = shlex.split(cmd)[0]\n\n        cmd = os.path.basename(cmd.lower())\n        for player, possiblecmds in SUPPORTED_PLAYERS.items():\n            for possiblecmd in possiblecmds:\n                if cmd.startswith(possiblecmd):\n                    return player\n\n    @classmethod\n    def _mpv_title_escape(cls, title_string):\n        # mpv has a \"disable property-expansion\" token which must be handled\n        # in order to accurately represent $$ in title\n        if r'\\$>' in title_string:\n            processed_title = \"\"\n            double_dollars = True\n            i = dollars = 0\n            while i < len(title_string):\n                if double_dollars:\n                    if title_string[i] == \"\\\\\":\n                        if title_string[i + 1] == \"$\":\n                            processed_title += \"$\"\n                            dollars += 1\n                            i += 1\n                            if title_string[i + 1] == \">\" and dollars % 2 == 1:\n                                double_dollars = False\n                                processed_title += \">\"\n                                i += 1\n                        else:\n                            processed_title += \"\\\\\"\n                    elif title_string[i] == \"$\":\n                        processed_title += \"$$\"\n                    else:\n                        dollars = 0\n                        processed_title += title_string[i]\n                else:\n                    if title_string[i:i + 2] == \"\\\\$\":\n                        processed_title += \"$\"\n                        i += 1\n                    else:\n                        processed_title += title_string[i]\n                i += 1\n            return processed_title\n        else:\n            # not possible for property-expansion to be disabled, happy days\n            return title_string.replace(\"$\", \"$$\").replace(r'\\$$', \"$\")\n\n    def _create_arguments(self):\n        if self.namedpipe:\n            filename = self.namedpipe.path\n        elif self.filename:\n            filename = self.filename\n        elif self.http:\n            filename = self.http.url\n        else:\n            filename = \"-\"\n        extra_args = []\n\n        if self.title is not None:\n            # vlc\n            if self.player_name == \"vlc\":\n                # see https://wiki.videolan.org/Documentation:Format_String/, allow escaping with \\$\n                self.title = self.title.replace(\"$\", \"$$\").replace(r'\\$$', \"$\")\n                extra_args.extend([\"--input-title-format\", self.title])\n\n            # mpv\n            if self.player_name == \"mpv\":\n                # see https://mpv.io/manual/stable/#property-expansion, allow escaping with \\$, respect mpv's $>\n                self.title = self._mpv_title_escape(self.title)\n                extra_args.append(f\"--force-media-title={self.title}\")\n\n            # potplayer\n            if self.player_name == \"potplayer\":\n                if filename != \"-\":\n                    # PotPlayer - About - Command Line\n                    # You can specify titles for URLs by separating them with a backslash (\\) at the end of URLs.\n                    # eg. \"http://...\\title of this url\"\n                    self.title = self.title.replace('\"', '')\n                    filename = filename[:-1] + '\\\\' + self.title + filename[-1]\n\n        args = self.args.format(**{PLAYER_ARGS_INPUT_DEFAULT: filename, PLAYER_ARGS_INPUT_FALLBACK: filename})\n        cmd = self.cmd\n\n        # player command\n        if is_win32:\n            eargs = subprocess.list2cmdline(extra_args)\n            # do not insert and extra \" \" when there are no extra_args\n            return \" \".join([cmd] + ([eargs] if eargs else []) + [args])\n        return shlex.split(cmd) + extra_args + shlex.split(args)\n\n    def _open(self):\n        try:\n            if self.record:\n                self.record.open()\n            if self.call and self.filename:\n                self._open_call()\n            else:\n                self._open_subprocess()\n        finally:\n            if self.quiet:\n                # Output streams no longer needed in parent process\n                self.stdout.close()\n                self.stderr.close()\n\n    def _open_call(self):\n        args = self._create_arguments()\n        if is_win32:\n            fargs = args\n        else:\n            fargs = subprocess.list2cmdline(args)\n        log.debug(f\"Calling: {fargs}\")\n\n        subprocess.call(args,\n                        stdout=self.stdout,\n                        stderr=self.stderr)\n\n    def _open_subprocess(self):\n        # Force bufsize=0 on all Python versions to avoid writing the\n        # unflushed buffer when closing a broken input pipe\n        args = self._create_arguments()\n        if is_win32:\n            fargs = args\n        else:\n            fargs = subprocess.list2cmdline(args)\n        log.debug(f\"Opening subprocess: {fargs}\")\n\n        self.player = subprocess.Popen(args,\n                                       stdin=self.stdin, bufsize=0,\n                                       stdout=self.stdout,\n                                       stderr=self.stderr)\n        # Wait 0.5 seconds to see if program exited prematurely\n        if not self.running:\n            raise OSError(\"Process exited prematurely\")\n\n        if self.namedpipe:\n            self.namedpipe.open(\"wb\")\n        elif self.http:\n            self.http.open()\n\n    def _close(self):\n        # Close input to the player first to signal the end of the\n        # stream and allow the player to terminate of its own accord\n        if self.namedpipe:\n            self.namedpipe.close()\n        elif self.http:\n            self.http.close()\n        elif not self.filename:\n            self.player.stdin.close()\n\n        if self.record:\n            self.record.close()\n\n        if self.kill:\n            with ignored(Exception):\n                self.player.terminate()\n                if not is_win32:\n                    t, timeout = 0.0, self.PLAYER_TERMINATE_TIMEOUT\n                    while self.player.poll() is None and t < timeout:\n                        sleep(0.5)\n                        t += 0.5\n\n                    if not self.player.returncode:\n                        self.player.kill()\n        self.player.wait()\n\n    def _write(self, data):\n        if self.record:\n            self.record.write(data)\n\n        if self.namedpipe:\n            self.namedpipe.write(data)\n        elif self.http:\n            self.http.write(data)\n        else:\n            self.player.stdin.write(data)\n\n\n__all__ = [\"PlayerOutput\", \"FileOutput\"]\n", "path": "src/streamlink_cli/output.py"}], "after_files": [{"content": "import logging\nimport os\nimport re\nimport shlex\nimport subprocess\nimport sys\nfrom time import sleep\n\nfrom streamlink_cli.compat import is_win32, stdout\nfrom streamlink_cli.constants import PLAYER_ARGS_INPUT_DEFAULT, PLAYER_ARGS_INPUT_FALLBACK, SUPPORTED_PLAYERS\nfrom streamlink_cli.utils import ignored\n\nif is_win32:\n    import msvcrt\n\nlog = logging.getLogger(\"streamlink.cli.output\")\n\n\nclass Output:\n    def __init__(self):\n        self.opened = False\n\n    def open(self):\n        self._open()\n        self.opened = True\n\n    def close(self):\n        if self.opened:\n            self._close()\n\n        self.opened = False\n\n    def write(self, data):\n        if not self.opened:\n            raise OSError(\"Output is not opened\")\n\n        return self._write(data)\n\n    def _open(self):\n        pass\n\n    def _close(self):\n        pass\n\n    def _write(self, data):\n        pass\n\n\nclass FileOutput(Output):\n    def __init__(self, filename=None, fd=None, record=None):\n        super().__init__()\n        self.filename = filename\n        self.fd = fd\n        self.record = record\n\n    def _open(self):\n        if self.filename:\n            self.fd = open(self.filename, \"wb\")\n\n        if self.record:\n            self.record.open()\n\n        if is_win32:\n            msvcrt.setmode(self.fd.fileno(), os.O_BINARY)\n\n    def _close(self):\n        if self.fd is not stdout:\n            self.fd.close()\n        if self.record:\n            self.record.close()\n\n    def _write(self, data):\n        self.fd.write(data)\n        if self.record:\n            self.record.write(data)\n\n\nclass PlayerOutput(Output):\n    PLAYER_TERMINATE_TIMEOUT = 10.0\n\n    _re_player_args_input = re.compile(\"|\".join(map(\n        lambda const: re.escape(f\"{{{const}}}\"),\n        [PLAYER_ARGS_INPUT_DEFAULT, PLAYER_ARGS_INPUT_FALLBACK]\n    )))\n\n    def __init__(self, cmd, args=\"\", filename=None, quiet=True, kill=True,\n                 call=False, http=None, namedpipe=None, record=None, title=None):\n        super().__init__()\n        self.cmd = cmd\n        self.args = args\n        self.kill = kill\n        self.call = call\n        self.quiet = quiet\n\n        self.filename = filename\n        self.namedpipe = namedpipe\n        self.http = http\n        self.title = title\n        self.player = None\n        self.player_name = self.supported_player(self.cmd)\n        self.record = record\n\n        if self.namedpipe or self.filename or self.http:\n            self.stdin = sys.stdin\n        else:\n            self.stdin = subprocess.PIPE\n\n        if self.quiet:\n            self.stdout = open(os.devnull, \"w\")\n            self.stderr = open(os.devnull, \"w\")\n        else:\n            self.stdout = sys.stdout\n            self.stderr = sys.stderr\n\n        if not self._re_player_args_input.search(self.args):\n            self.args += f\"{' ' if self.args else ''}{{{PLAYER_ARGS_INPUT_DEFAULT}}}\"\n\n    @property\n    def running(self):\n        sleep(0.5)\n        return self.player.poll() is None\n\n    @classmethod\n    def supported_player(cls, cmd):\n        \"\"\"\n        Check if the current player supports adding a title\n\n        :param cmd: command to test\n        :return: name of the player|None\n        \"\"\"\n        if not is_win32:\n            # under a POSIX system use shlex to find the actual command\n            # under windows this is not an issue because executables end in .exe\n            cmd = shlex.split(cmd)[0]\n\n        cmd = os.path.basename(cmd.lower())\n        for player, possiblecmds in SUPPORTED_PLAYERS.items():\n            for possiblecmd in possiblecmds:\n                if cmd.startswith(possiblecmd):\n                    return player\n\n    @classmethod\n    def _mpv_title_escape(cls, title_string):\n        # mpv has a \"disable property-expansion\" token which must be handled\n        # in order to accurately represent $$ in title\n        if r'\\$>' in title_string:\n            processed_title = \"\"\n            double_dollars = True\n            i = dollars = 0\n            while i < len(title_string):\n                if double_dollars:\n                    if title_string[i] == \"\\\\\":\n                        if title_string[i + 1] == \"$\":\n                            processed_title += \"$\"\n                            dollars += 1\n                            i += 1\n                            if title_string[i + 1] == \">\" and dollars % 2 == 1:\n                                double_dollars = False\n                                processed_title += \">\"\n                                i += 1\n                        else:\n                            processed_title += \"\\\\\"\n                    elif title_string[i] == \"$\":\n                        processed_title += \"$$\"\n                    else:\n                        dollars = 0\n                        processed_title += title_string[i]\n                else:\n                    if title_string[i:i + 2] == \"\\\\$\":\n                        processed_title += \"$\"\n                        i += 1\n                    else:\n                        processed_title += title_string[i]\n                i += 1\n            return processed_title\n        else:\n            # not possible for property-expansion to be disabled, happy days\n            return title_string.replace(\"$\", \"$$\").replace(r'\\$$', \"$\")\n\n    def _create_arguments(self):\n        if self.namedpipe:\n            filename = self.namedpipe.path\n            if is_win32:\n                if self.player_name == \"vlc\":\n                    filename = f\"stream://\\\\{filename}\"\n                elif self.player_name == \"mpv\":\n                    filename = f\"file://{filename}\"\n        elif self.filename:\n            filename = self.filename\n        elif self.http:\n            filename = self.http.url\n        else:\n            filename = \"-\"\n        extra_args = []\n\n        if self.title is not None:\n            # vlc\n            if self.player_name == \"vlc\":\n                # see https://wiki.videolan.org/Documentation:Format_String/, allow escaping with \\$\n                self.title = self.title.replace(\"$\", \"$$\").replace(r'\\$$', \"$\")\n                extra_args.extend([\"--input-title-format\", self.title])\n\n            # mpv\n            if self.player_name == \"mpv\":\n                # see https://mpv.io/manual/stable/#property-expansion, allow escaping with \\$, respect mpv's $>\n                self.title = self._mpv_title_escape(self.title)\n                extra_args.append(f\"--force-media-title={self.title}\")\n\n            # potplayer\n            if self.player_name == \"potplayer\":\n                if filename != \"-\":\n                    # PotPlayer - About - Command Line\n                    # You can specify titles for URLs by separating them with a backslash (\\) at the end of URLs.\n                    # eg. \"http://...\\title of this url\"\n                    self.title = self.title.replace('\"', '')\n                    filename = filename[:-1] + '\\\\' + self.title + filename[-1]\n\n        args = self.args.format(**{PLAYER_ARGS_INPUT_DEFAULT: filename, PLAYER_ARGS_INPUT_FALLBACK: filename})\n        cmd = self.cmd\n\n        # player command\n        if is_win32:\n            eargs = subprocess.list2cmdline(extra_args)\n            # do not insert and extra \" \" when there are no extra_args\n            return \" \".join([cmd] + ([eargs] if eargs else []) + [args])\n        return shlex.split(cmd) + extra_args + shlex.split(args)\n\n    def _open(self):\n        try:\n            if self.record:\n                self.record.open()\n            if self.call and self.filename:\n                self._open_call()\n            else:\n                self._open_subprocess()\n        finally:\n            if self.quiet:\n                # Output streams no longer needed in parent process\n                self.stdout.close()\n                self.stderr.close()\n\n    def _open_call(self):\n        args = self._create_arguments()\n        if is_win32:\n            fargs = args\n        else:\n            fargs = subprocess.list2cmdline(args)\n        log.debug(f\"Calling: {fargs}\")\n\n        subprocess.call(args,\n                        stdout=self.stdout,\n                        stderr=self.stderr)\n\n    def _open_subprocess(self):\n        # Force bufsize=0 on all Python versions to avoid writing the\n        # unflushed buffer when closing a broken input pipe\n        args = self._create_arguments()\n        if is_win32:\n            fargs = args\n        else:\n            fargs = subprocess.list2cmdline(args)\n        log.debug(f\"Opening subprocess: {fargs}\")\n\n        self.player = subprocess.Popen(args,\n                                       stdin=self.stdin, bufsize=0,\n                                       stdout=self.stdout,\n                                       stderr=self.stderr)\n        # Wait 0.5 seconds to see if program exited prematurely\n        if not self.running:\n            raise OSError(\"Process exited prematurely\")\n\n        if self.namedpipe:\n            self.namedpipe.open(\"wb\")\n        elif self.http:\n            self.http.open()\n\n    def _close(self):\n        # Close input to the player first to signal the end of the\n        # stream and allow the player to terminate of its own accord\n        if self.namedpipe:\n            self.namedpipe.close()\n        elif self.http:\n            self.http.close()\n        elif not self.filename:\n            self.player.stdin.close()\n\n        if self.record:\n            self.record.close()\n\n        if self.kill:\n            with ignored(Exception):\n                self.player.terminate()\n                if not is_win32:\n                    t, timeout = 0.0, self.PLAYER_TERMINATE_TIMEOUT\n                    while self.player.poll() is None and t < timeout:\n                        sleep(0.5)\n                        t += 0.5\n\n                    if not self.player.returncode:\n                        self.player.kill()\n        self.player.wait()\n\n    def _write(self, data):\n        if self.record:\n            self.record.write(data)\n\n        if self.namedpipe:\n            self.namedpipe.write(data)\n        elif self.http:\n            self.http.write(data)\n        else:\n            self.player.stdin.write(data)\n\n\n__all__ = [\"PlayerOutput\", \"FileOutput\"]\n", "path": "src/streamlink_cli/output.py"}]}