Repository: streamlink/streamlink

Issue:
plugins.showroom: The rules of room URLs changed recently and Streamlink cannot catch the stream
### Checklist

- [X] This is a plugin issue 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 issues](https://github.com/streamlink/streamlink/issues?q=is%3Aissue+label%3A%22plugin+issue%22)
- [X] [I have checked the commit log of the master branch](https://github.com/streamlink/streamlink/commits/master)

### Streamlink version

Latest build from the master branch

### Description

The (official certified) individual room URL of SHOWROOM was like:
https://www.showroom-live.com/ROOM_NAME

But recently (maybe just today) it has been changed to:
https://www.showroom-live.com/r/ROOM_NAME

Streamlink worked well on this site (until April 18th) before the URL rules changed, but now it cannot catch the stream using either of the URLs above, only showing:
![image](https://user-images.githubusercontent.com/48208459/163987392-1eeef378-1512-4bca-a1fb-af9c1bdb0376.png)
![image](https://user-images.githubusercontent.com/48208459/163987776-b310005f-72ca-4303-8985-9e8da01ad195.png)

While at the same time, this room is indeed on streaming:
![image](https://user-images.githubusercontent.com/48208459/163987622-9fbeb464-7257-4563-a62e-36bec0928b76.png)



### Debug log

```text
[cli][debug] OS:         Windows 10
[cli][debug] Python:     3.9.10
[cli][debug] Streamlink: 3.2.0
[cli][debug] Requests(2.27.1), Socks(1.7.1), Websocket(1.2.3)
[cli][debug] Arguments:
[cli][debug]  url=https://www.showroom-live.com/r/46_endosakura
[cli][debug]  stream=['best']
[cli][debug]  --loglevel=debug
[cli][debug]  --locale=ja_JP
[cli][debug]  --player=C:\Program Files (x86)\Pure Codec\x64\PotPlayerMini64.exe
[cli][debug]  --player-no-close=True
[cli][debug]  --retry-streams=3.0
[cli][debug]  --stream-segment-threads=10
[cli][debug]  --stream-timeout=600.0
[cli][debug]  --hls-live-restart=True
[cli][debug]  --hls-segment-threads=10
[cli][debug]  --ffmpeg-ffmpeg=ffmpeg.exe
[cli][debug]  --http-proxy=http://127.0.0.1:7890
[cli][debug]  --http-header=[('referer', '')]
[cli][debug]  --twitch-disable-ads=True
[cli][info] Found matching plugin showroom for URL https://www.showroom-live.com/r/46_endosakura
[cli][info] Waiting for streams, retrying every 3.0 second(s)
```



Discussion:

Resolving patch:
diff --git a/src/streamlink/plugins/showroom.py b/src/streamlink/plugins/showroom.py
--- a/src/streamlink/plugins/showroom.py
+++ b/src/streamlink/plugins/showroom.py
@@ -1,5 +1,5 @@
 """
-$description Japanese live streaming service used primarily by Japanese idols & voice actors and their fans.
+$description Japanese live-streaming service used primarily by Japanese idols & voice actors and their fans.
 $url showroom-live.com
 $type live
 """
@@ -9,53 +9,64 @@
 
 from streamlink.plugin import Plugin, pluginmatcher
 from streamlink.plugin.api import validate
-from streamlink.stream.hls import HLSStream, HLSStreamReader, HLSStreamWorker
+from streamlink.stream.hls import HLSStream
 
 log = logging.getLogger(__name__)
 
 
-class ShowroomHLSStreamWorker(HLSStreamWorker):
-    def _playlist_reload_time(self, playlist, sequences):
-        return 1.5
-
-
-class ShowroomHLSStreamReader(HLSStreamReader):
-    __worker__ = ShowroomHLSStreamWorker
-
-
-class ShowroomHLSStream(HLSStream):
-    __reader__ = ShowroomHLSStreamReader
-
-
 @pluginmatcher(re.compile(
     r"https?://(?:\w+\.)?showroom-live\.com/"
 ))
 class Showroom(Plugin):
+    LIVE_STATUS = 2
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.session.set_option("hls-playlist-reload-time", "segment")
+
     def _get_streams(self):
-        data = self.session.http.get(
+        re_room_id = re.compile(r"share_url:\"https:[^?]+?\?room_id=(?P<room_id>\d+)\"")
+        room_id = self.session.http.get(
             self.url,
             schema=validate.Schema(
                 validate.parse_html(),
-                validate.xml_xpath_string(".//script[@id='js-live-data'][@data-json]/@data-json"),
+                validate.xml_xpath_string(".//script[contains(text(),'share_url:\"https:')][1]/text()"),
                 validate.any(None, validate.all(
-                    validate.parse_json(),
-                    {"is_live": int,
-                     "room_id": int,
-                     validate.optional("room"): {"content_region_permission": int, "is_free": int}},
+                    validate.transform(re_room_id.search),
+                    validate.any(None, validate.get("room_id"))
                 ))
             )
         )
-        if not data:  # URL without livestream
+        if not room_id:
             return
 
-        log.debug(f"{data!r}")
-        if data["is_live"] != 1:
+        live_status, self.title = self.session.http.get(
+            "https://www.showroom-live.com/api/live/live_info",
+            params={
+                "room_id": room_id
+            },
+            schema=validate.Schema(
+                validate.parse_json(),
+                {
+                    "live_status": int,
+                    "room_name": str,
+                },
+                validate.union_get(
+                    "live_status",
+                    "room_name",
+                )
+            )
+        )
+        if live_status != self.LIVE_STATUS:
             log.info("This stream is currently offline")
             return
 
         url = self.session.http.get(
             "https://www.showroom-live.com/api/live/streaming_url",
-            params={"room_id": data["room_id"], "abr_available": 1},
+            params={
+                "room_id": room_id,
+                "abr_available": 1,
+            },
             schema=validate.Schema(
                 validate.parse_json(),
                 {"streaming_url_list": [{
@@ -67,11 +78,13 @@ def _get_streams(self):
                 validate.get((0, "url"))
             ),
         )
+
         res = self.session.http.get(url, acceptable_status=(200, 403, 404))
         if res.headers["Content-Type"] != "application/x-mpegURL":
             log.error("This stream is restricted")
             return
-        return ShowroomHLSStream.parse_variant_playlist(self.session, url)
+
+        return HLSStream.parse_variant_playlist(self.session, url)
 
 
 __plugin__ = Showroom


Test patch: