prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Less violent way of closing player when stream ends
  Currently streamlink uses SIGKILL to close the player when a stream ends. This prevents the player from doing its own cleanup. For example, mpv leaves DPMS/screensaver disabled because of this. I know there is --player-no-close option, but that has an unwanted side-effect of not closing the player immediately in some situations.
  
  I suggest fixing it by using SIGTERM instead:
  ```diff
  diff -bur streamlink-0.1.0-orig/src/streamlink_cli/output.py streamlink-0.1.0/src/streamlink_cli/output.py
  --- streamlink-0.1.0-orig/src/streamlink_cli/output.py  2016-11-21 21:56:29.000000000 +0200
  +++ streamlink-0.1.0/src/streamlink_cli/output.py       2016-12-08 22:08:23.000000000 +0200
  @@ -161,7 +161,7 @@
   
           if self.kill:
               with ignored(Exception):
  -                self.player.kill()
  +                self.player.terminate()
           self.player.wait()
   
       def _write(self, data):
  ```
  
  --- 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 os
  2 import shlex
  3 import subprocess
  4 import sys
  5 
  6 from time import sleep
  7 
  8 import re
  9 
  10 from .compat import is_win32, stdout
  11 from .constants import DEFAULT_PLAYER_ARGUMENTS
  12 from .utils import ignored
  13 
  14 if is_win32:
  15     import msvcrt
  16 
  17 
  18 class Output(object):
  19     def __init__(self):
  20         self.opened = False
  21 
  22     def open(self):
  23         self._open()
  24         self.opened = True
  25 
  26     def close(self):
  27         if self.opened:
  28             self._close()
  29 
  30         self.opened = False
  31 
  32     def write(self, data):
  33         if not self.opened:
  34             raise IOError("Output is not opened")
  35 
  36         return self._write(data)
  37 
  38     def _open(self):
  39         pass
  40 
  41     def _close(self):
  42         pass
  43 
  44     def _write(self, data):
  45         pass
  46 
  47 
  48 class FileOutput(Output):
  49     def __init__(self, filename=None, fd=None):
  50         super(FileOutput, self).__init__()
  51         self.filename = filename
  52         self.fd = fd
  53 
  54     def _open(self):
  55         if self.filename:
  56             self.fd = open(self.filename, "wb")
  57 
  58         if is_win32:
  59             msvcrt.setmode(self.fd.fileno(), os.O_BINARY)
  60 
  61     def _close(self):
  62         if self.fd is not stdout:
  63             self.fd.close()
  64 
  65     def _write(self, data):
  66         self.fd.write(data)
  67 
  68 
  69 class PlayerOutput(Output):
  70     def __init__(self, cmd, args=DEFAULT_PLAYER_ARGUMENTS, filename=None, quiet=True, kill=True, call=False, http=False,
  71                  namedpipe=None):
  72         super(PlayerOutput, self).__init__()
  73         self.cmd = cmd
  74         self.args = args
  75         self.kill = kill
  76         self.call = call
  77         self.quiet = quiet
  78 
  79         self.filename = filename
  80         self.namedpipe = namedpipe
  81         self.http = http
  82 
  83         if self.namedpipe or self.filename or self.http:
  84             self.stdin = sys.stdin
  85         else:
  86             self.stdin = subprocess.PIPE
  87 
  88         if self.quiet:
  89             self.stdout = open(os.devnull, "w")
  90             self.stderr = open(os.devnull, "w")
  91         else:
  92             self.stdout = sys.stdout
  93             self.stderr = sys.stderr
  94 
  95     @property
  96     def running(self):
  97         sleep(0.5)
  98         self.player.poll()
  99         return self.player.returncode is None
  100 
  101     def _create_arguments(self):
  102         if self.namedpipe:
  103             filename = self.namedpipe.path
  104         elif self.filename:
  105             filename = self.filename
  106         elif self.http:
  107             filename = self.http.url
  108         else:
  109             filename = "-"
  110 
  111         args = self.args.format(filename=filename)
  112         cmd = self.cmd
  113         if is_win32:
  114             return cmd + " " + args
  115 
  116         return shlex.split(cmd) + shlex.split(args)
  117 
  118     def _open(self):
  119         try:
  120             if self.call and self.filename:
  121                 self._open_call()
  122             else:
  123                 self._open_subprocess()
  124         finally:
  125             if self.quiet:
  126                 # Output streams no longer needed in parent process
  127                 self.stdout.close()
  128                 self.stderr.close()
  129 
  130     def _open_call(self):
  131         subprocess.call(self._create_arguments(),
  132                         stdout=self.stdout,
  133                         stderr=self.stderr)
  134 
  135     def _open_subprocess(self):
  136         # Force bufsize=0 on all Python versions to avoid writing the
  137         # unflushed buffer when closing a broken input pipe
  138         self.player = subprocess.Popen(self._create_arguments(),
  139                                        stdin=self.stdin, bufsize=0,
  140                                        stdout=self.stdout,
  141                                        stderr=self.stderr)
  142         # Wait 0.5 seconds to see if program exited prematurely
  143         if not self.running:
  144             raise OSError("Process exited prematurely")
  145 
  146         if self.namedpipe:
  147             self.namedpipe.open("wb")
  148         elif self.http:
  149             self.http.open()
  150 
  151     def _close(self):
  152         # Close input to the player first to signal the end of the
  153         # stream and allow the player to terminate of its own accord
  154         if self.namedpipe:
  155             self.namedpipe.close()
  156         elif self.http:
  157             self.http.close()
  158         elif not self.filename:
  159             self.player.stdin.close()
  160 
  161         if self.kill:
  162             with ignored(Exception):
  163                 self.player.kill()
  164         self.player.wait()
  165 
  166     def _write(self, data):
  167         if self.namedpipe:
  168             self.namedpipe.write(data)
  169         elif self.http:
  170             self.http.write(data)
  171         else:
  172             self.player.stdin.write(data)
  173 
  174 
  175 __all__ = ["PlayerOutput", "FileOutput"]
  176
  ```
  --- 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()
  
  ```
  
golden_diff: 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
  @@ -67,6 +67,8 @@
   
   
   class PlayerOutput(Output):
  +    PLAYER_TERMINATE_TIMEOUT = 10.0
  +
       def __init__(self, cmd, args=DEFAULT_PLAYER_ARGUMENTS, filename=None, quiet=True, kill=True, call=False, http=False,
                    namedpipe=None):
           super(PlayerOutput, self).__init__()
  @@ -160,7 +162,15 @@
   
           if self.kill:
               with ignored(Exception):
  -                self.player.kill()
  +                self.player.terminate()
  +                if not is_win32:
  +                    t, timeout = 0.0, self.PLAYER_TERMINATE_TIMEOUT
  +                    while not self.player.poll() and t < timeout:
  +                        sleep(0.5)
  +                        t += 0.5
  +
  +                    if not self.player.returncode:
  +                        self.player.kill()
           self.player.wait()
   
       def _write(self, data):
  
verification_info: {"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@@ -67,6 +67,8 @@\n \n \n class PlayerOutput(Output):\n+    PLAYER_TERMINATE_TIMEOUT = 10.0\n+\n     def __init__(self, cmd, args=DEFAULT_PLAYER_ARGUMENTS, filename=None, quiet=True, kill=True, call=False, http=False,\n                  namedpipe=None):\n         super(PlayerOutput, self).__init__()\n@@ -160,7 +162,15 @@\n \n         if self.kill:\n             with ignored(Exception):\n-                self.player.kill()\n+                self.player.terminate()\n+                if not is_win32:\n+                    t, timeout = 0.0, self.PLAYER_TERMINATE_TIMEOUT\n+                    while not self.player.poll() 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", "issue": "Less violent way of closing player when stream ends\nCurrently streamlink uses SIGKILL to close the player when a stream ends. This prevents the player from doing its own cleanup. For example, mpv leaves DPMS/screensaver disabled because of this. I know there is --player-no-close option, but that has an unwanted side-effect of not closing the player immediately in some situations.\r\n\r\nI suggest fixing it by using SIGTERM instead:\r\n```diff\r\ndiff -bur streamlink-0.1.0-orig/src/streamlink_cli/output.py streamlink-0.1.0/src/streamlink_cli/output.py\r\n--- streamlink-0.1.0-orig/src/streamlink_cli/output.py  2016-11-21 21:56:29.000000000 +0200\r\n+++ streamlink-0.1.0/src/streamlink_cli/output.py       2016-12-08 22:08:23.000000000 +0200\r\n@@ -161,7 +161,7 @@\r\n \r\n         if self.kill:\r\n             with ignored(Exception):\r\n-                self.player.kill()\r\n+                self.player.terminate()\r\n         self.player.wait()\r\n \r\n     def _write(self, data):\r\n```\n", "before_files": [{"content": "import os\nimport shlex\nimport subprocess\nimport sys\n\nfrom time import sleep\n\nimport re\n\nfrom .compat import is_win32, stdout\nfrom .constants import DEFAULT_PLAYER_ARGUMENTS\nfrom .utils import ignored\n\nif is_win32:\n    import msvcrt\n\n\nclass Output(object):\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 IOError(\"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):\n        super(FileOutput, self).__init__()\n        self.filename = filename\n        self.fd = fd\n\n    def _open(self):\n        if self.filename:\n            self.fd = open(self.filename, \"wb\")\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\n    def _write(self, data):\n        self.fd.write(data)\n\n\nclass PlayerOutput(Output):\n    def __init__(self, cmd, args=DEFAULT_PLAYER_ARGUMENTS, filename=None, quiet=True, kill=True, call=False, http=False,\n                 namedpipe=None):\n        super(PlayerOutput, self).__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\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    @property\n    def running(self):\n        sleep(0.5)\n        self.player.poll()\n        return self.player.returncode is None\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\n        args = self.args.format(filename=filename)\n        cmd = self.cmd\n        if is_win32:\n            return cmd + \" \" + args\n\n        return shlex.split(cmd) + shlex.split(args)\n\n    def _open(self):\n        try:\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        subprocess.call(self._create_arguments(),\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        self.player = subprocess.Popen(self._create_arguments(),\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.kill:\n            with ignored(Exception):\n                self.player.kill()\n        self.player.wait()\n\n    def _write(self, data):\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 os\nimport shlex\nimport subprocess\nimport sys\n\nfrom time import sleep\n\nimport re\n\nfrom .compat import is_win32, stdout\nfrom .constants import DEFAULT_PLAYER_ARGUMENTS\nfrom .utils import ignored\n\nif is_win32:\n    import msvcrt\n\n\nclass Output(object):\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 IOError(\"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):\n        super(FileOutput, self).__init__()\n        self.filename = filename\n        self.fd = fd\n\n    def _open(self):\n        if self.filename:\n            self.fd = open(self.filename, \"wb\")\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\n    def _write(self, data):\n        self.fd.write(data)\n\n\nclass PlayerOutput(Output):\n    PLAYER_TERMINATE_TIMEOUT = 10.0\n\n    def __init__(self, cmd, args=DEFAULT_PLAYER_ARGUMENTS, filename=None, quiet=True, kill=True, call=False, http=False,\n                 namedpipe=None):\n        super(PlayerOutput, self).__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\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    @property\n    def running(self):\n        sleep(0.5)\n        self.player.poll()\n        return self.player.returncode is None\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\n        args = self.args.format(filename=filename)\n        cmd = self.cmd\n        if is_win32:\n            return cmd + \" \" + args\n\n        return shlex.split(cmd) + shlex.split(args)\n\n    def _open(self):\n        try:\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        subprocess.call(self._create_arguments(),\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        self.player = subprocess.Popen(self._create_arguments(),\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.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 not self.player.poll() 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.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"}]}