Repository: streamlink/streamlink

Issue:
Support for password-protected TwitCasting streams
## Feature Request

- [x] This is a feature request and I have read the contribution guidelines.
- [x] I am using the latest development version from the master branch.


### Description

Using a command line option such as --twitcasting-password, you would be able to retrieve even password-protected TwitCasting streams.

### Expected / Actual behavior

Self-explanatory.


### Additional comments, etc.

Accessing a password-protected stream can be done by sending a POST request to the stream URL containing the form data `password=`.

Possibly of help is youtube-dl's implementation: https://github.com/ytdl-org/youtube-dl/pull/20843/files

For help testing this feature, here is an archived, password-protected stream: https://twitcasting.tv/mttbernardini/movie/3689740

If a live broadcast is needed, you can easily start a livestream from Twitcasting by hovering the blue "Broadcast" button at the top right of the site then clicking "Web broadcasting". Scroll down to set a password then back up to start an audio-only livestream.


[Love Streamlink? Please consider supporting our collective. Thanks!](https://opencollective.com/streamlink/donate)



Discussion:

Resolving patch:
diff --git a/src/streamlink/plugins/twitcasting.py b/src/streamlink/plugins/twitcasting.py
--- a/src/streamlink/plugins/twitcasting.py
+++ b/src/streamlink/plugins/twitcasting.py
@@ -1,3 +1,4 @@
+import hashlib
 import logging
 import re
 from threading import Event, Thread
@@ -7,15 +8,25 @@
 
 from streamlink import logger
 from streamlink.buffers import RingBuffer
-from streamlink.plugin import Plugin, PluginError
+from streamlink.plugin import Plugin, PluginArgument, PluginArguments, PluginError
 from streamlink.plugin.api import useragents, validate
 from streamlink.stream.stream import Stream
 from streamlink.stream.stream import StreamIO
+from streamlink.utils.url import update_qsd
+
 
 log = logging.getLogger(__name__)
 
 
 class TwitCasting(Plugin):
+    arguments = PluginArguments(
+        PluginArgument(
+            "password",
+            sensitive=True,
+            metavar="PASSWORD",
+            help="Password for private Twitcasting streams."
+        )
+    )
     _url_re = re.compile(r"http(s)?://twitcasting.tv/(?P<channel>[^/]+)", re.VERBOSE)
     _STREAM_INFO_URL = "https://twitcasting.tv/streamserver.php?target={channel}&mode=client"
     _STREAM_REAL_URL = "{proto}://{host}/ws.app/stream/{movie_id}/fmp4/bd/1/1500?mode={mode}"
@@ -66,6 +77,12 @@ def _get_streams(self):
             raise PluginError("No stream available for user {}".format(self.channel))
 
         real_stream_url = self._STREAM_REAL_URL.format(proto=proto, host=host, movie_id=movie_id, mode=mode)
+
+        password = self.options.get("password")
+        if password is not None:
+            password_hash = hashlib.md5(password.encode()).hexdigest()
+            real_stream_url = update_qsd(real_stream_url, {"word": password_hash})
+
         log.debug("Real stream url: {}".format(real_stream_url))
 
         return {mode: TwitCastingStream(session=self.session, url=real_stream_url)}


Test patch: