Repository: streamlink/streamlink

Issue:
cli: option for running --player-external-http in "non-continuous" mode
### Checklist

- [X] This is a feature request and not a different kind of issue
- [X] [I have read the contribution guidelines](https://github.com/streamlink/streamlink/blob/master/CONTRIBUTING.md#contributing-to-streamlink)
- [X] [I have checked the list of open and recently closed plugin requests](https://github.com/streamlink/streamlink/issues?q=is%3Aissue+label%3A%22feature+request%22)

### Description

I frequently run streamlink with the `player-external-http` flags within a tmux session to have the ability to swap between devices. This works quite well, but I'll occasionally encounter errors where streamlink is still running idle off in a session somewhere long after the target stream has wrapped up. It would be nice if we could be able to customize behavior of the app when a target stream ends (or is down for X amount of time to allow for unintended disconnects on the source stream's end). 

The current behavior is largely fine for desktop use, but the ability to get more granular control would be appreciated for headless use cases.


Discussion:

Resolving patch:
diff --git a/src/streamlink_cli/argparser.py b/src/streamlink_cli/argparser.py
--- a/src/streamlink_cli/argparser.py
+++ b/src/streamlink_cli/argparser.py
@@ -482,14 +482,33 @@ def build_parser():
         useful to allow external devices like smartphones or streaming boxes to
         watch streams they wouldn't be able to otherwise.
 
-        Behavior will be similar to the continuous HTTP option, but no player
-        program will be started, and the server will listen on all available
+        The default behavior is similar to the --player-continuous-http option,
+        but no player program will be started, and the server will listen on all available
         connections instead of just in the local (loopback) interface.
 
+        Optionally, the --player-external-http-continuous option allows for disabling
+        the continuous run-mode, so that Streamlink will stop when the stream ends.
+
         The URLs that can be used to access the stream will be printed to the
         console, and the server can be interrupted using CTRL-C.
         """
     )
+    player.add_argument(
+        "--player-external-http-continuous",
+        type=boolean,
+        metavar="{yes,true,1,on,no,false,0,off}",
+        default=True,
+        help="""
+        Set the run-mode of --player-external-http to continuous or non-continuous.
+
+        In the continuous run-mode, Streamlink will keep running after the stream has ended
+        and will wait for the next HTTP request being made unless it gets shut down via CTRL-C.
+
+        If set to non-continuous, Streamlink will stop once the stream has ended.
+
+        Default is true.
+        """
+    )
     player.add_argument(
         "--player-external-http-port",
         metavar="PORT",
diff --git a/src/streamlink_cli/main.py b/src/streamlink_cli/main.py
--- a/src/streamlink_cli/main.py
+++ b/src/streamlink_cli/main.py
@@ -197,6 +197,7 @@ def output_stream_http(
     initial_streams: Dict[str, Stream],
     formatter: Formatter,
     external: bool = False,
+    continuous: bool = True,
     port: int = 0,
 ):
     """Continuously output the stream over HTTP."""
@@ -267,6 +268,9 @@ def output_stream_http(
             log.debug("Writing stream to player")
             read_stream(stream_fd, server, prebuffer, formatter)
 
+        if not continuous:
+            break
+
         server.close(True)
 
     if player:
@@ -474,8 +478,14 @@ def handle_stream(plugin: Plugin, streams: Dict[str, Stream], stream_name: str)
                 log.info(f"Opening stream: {stream_name} ({stream_type})")
                 success = output_stream_passthrough(stream, formatter)
             elif args.player_external_http:
-                return output_stream_http(plugin, streams, formatter, external=True,
-                                          port=args.player_external_http_port)
+                return output_stream_http(
+                    plugin,
+                    streams,
+                    formatter,
+                    external=True,
+                    continuous=args.player_external_http_continuous,
+                    port=args.player_external_http_port,
+                )
             elif args.player_continuous_http and not file_output:
                 return output_stream_http(plugin, streams, formatter)
             else:


Test patch: