"""
$description Japanese live-streaming service used primarily by Japanese idols & voice actors and their fans.
$url showroom-live.com
$type live
$metadata title
"""

import logging
import re
from urllib.parse import parse_qsl, urlparse

from streamlink.plugin import Plugin, pluginmatcher
from streamlink.plugin.api import validate
from streamlink.stream.hls import HLSStream


log = logging.getLogger(__name__)


@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):
        room_id = self.session.http.get(
            self.url,
            schema=validate.Schema(
                validate.parse_html(),
                validate.xml_xpath_string(".//nav//a[contains(@href,'/room/profile?')]/@href"),
                validate.none_or_all(
                    validate.transform(lambda _url_profile: dict(parse_qsl(urlparse(_url_profile).query))),
                    validate.get("room_id"),
                ),
            ),
        )
        if not room_id:
            return

        log.debug(f"Room ID: {room_id}")

        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": room_id,
                "abr_available": 1,
            },
            schema=validate.Schema(
                validate.parse_json(),
                {"streaming_url_list": [{
                    "type": str,
                    "url": validate.url(),
                }]},
                validate.get("streaming_url_list"),
                validate.filter(lambda p: p["type"] == "hls_all"),
                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 HLSStream.parse_variant_playlist(self.session, url)


__plugin__ = Showroom

src/streamlink/plugins/showroom.py
"""
$description Japanese live-streaming service used primarily by Japanese idols & voice actors and their fans.
$url showroom-live.com
$type live
$metadata title
"""

import logging
import re
from urllib.parse import parse_qsl, urlparse

from streamlink.plugin import Plugin, pluginmatcher
from streamlink.plugin.api import validate
from streamlink.stream.hls import HLSStream


log = logging.getLogger(__name__)


@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):
        room_id = self.session.http.get(
            self.url,
            schema=validate.Schema(
                validate.parse_html(),
                validate.xml_xpath_string(".//nav//a[contains(@href,'/room/profile?')]/@href"),
                validate.none_or_all(
                    validate.transform(lambda _url_profile: dict(parse_qsl(urlparse(_url_profile).query))),
                    validate.get("room_id"),
                ),
            ),
        )
        if not room_id:
            return

        log.debug(f"Room ID: {room_id}")

        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": room_id,
                "abr_available": 1,
            },
            schema=validate.Schema(
                validate.parse_json(),
                {"streaming_url_list": [{
                    "type": str,
                    "url": validate.url(),
                }]},
                validate.get("streaming_url_list"),
                validate.filter(lambda p: p["type"] == "hls_all"),
                validate.get((0, "url")),
            ),
        )

        res = self.session.http.get(url, acceptable_status=(200, 403, 404))
        if res.headers["Content-Type"] not in ("application/x-mpegURL", "application/vnd.apple.mpegurl"):
            log.error("This stream is restricted")
            return

        return HLSStream.parse_variant_playlist(self.session, url)


__plugin__ = Showroom

src/streamlink/plugins/showroom.py
diff --git a/src/streamlink/plugins/showroom.py b/src/streamlink/plugins/showroom.py
index d06db84017d..98a91b24170 100644
--- a/src/streamlink/plugins/showroom.py
+++ b/src/streamlink/plugins/showroom.py
@@ -84,7 +84,7 @@ def _get_streams(self):
         )
 
         res = self.session.http.get(url, acceptable_status=(200, 403, 404))
-        if res.headers["Content-Type"] != "application/x-mpegURL":
+        if res.headers["Content-Type"] not in ("application/x-mpegURL", "application/vnd.apple.mpegurl"):
             log.error("This stream is restricted")
             return
 

plugins.showroom: streamlink unable to download any live streams from showroom.com
### Checklist

- [X] This is a [plugin issue](https://streamlink.github.io/plugins.html) and not [a different kind of issue](https://github.com/streamlink/streamlink/issues/new/choose)
- [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

streamlink 6.7.2

### Description

On 2024.03.29, showroom.com made some changes to their site.

When I try to use streamlink to record a showroom url that is online, for eg. https://www.showroom-live.com/r/48_KOJIMA_AIKO

>    streamlink --loglevel debug https://www.showroom-live.com/r/48_KOJIMA_AIKO best -o testing.ts

the expected behavior is that it should return this:

>  [cli][info] Found matching plugin showroom for URL https://www.showroom-live.com/r/48_KOJIMA_AIKO
    [utils.l10n][debug] Language code: en_US
    [cli][info] Available streams: 144p (worst), 360p (best)
 [cli][info] Opening stream: 360p (hls)
 [cli][info] Writing output to D:\testing.ts
 [cli][debug] Checking file output
 [stream.hls][debug] Reloading playlist
 [cli][debug] Pre-buffering 8192 bytes
 [stream.hls][debug] First Sequence: 1; Last Sequence: 4
 [stream.hls][debug] Start offset: 0; Duration: None; Start Sequence: 3; End Sequence: None
 [stream.hls][debug] Adding segment 3 to queue
 [stream.hls][debug] Adding segment 4 to queue

However, when I tried recording a showroom stream on 2024.03.29, I got an error stating that the stream is restricted.

> L:\>streamlink --loglevel debug https://www.showroom-live.com/r/48_KOJIMA_AIKO best -o sample.ts
[session][debug] Loading plugin: showroom
[cli][debug] OS:         Windows 10
[cli][debug] Python:     3.10.6
[cli][debug] OpenSSL:    OpenSSL 1.1.1n  15 Mar 2022
[cli][debug] Streamlink: 6.7.2
[cli][debug] Dependencies:
[cli][debug]  certifi: 2022.6.15
[cli][debug]  exceptiongroup: 1.2.0
[cli][debug]  isodate: 0.6.1
[cli][debug]  lxml: 4.9.1
[cli][debug]  pycountry: 22.3.5
[cli][debug]  pycryptodome: 3.16.0
[cli][debug]  PySocks: 1.7.1
[cli][debug]  requests: 2.28.2
[cli][debug]  trio: 0.25.0
[cli][debug]  trio-websocket: 0.11.1
[cli][debug]  typing-extensions: 4.5.0
[cli][debug]  urllib3: 1.26.12
[cli][debug]  websocket-client: 1.4.0
[cli][debug] Arguments:
[cli][debug]  url=https://www.showroom-live.com/r/48_KOJIMA_AIKO
[cli][debug]  stream=['best']
[cli][debug]  --loglevel=debug
[cli][debug]  --output=sample.ts
[cli][info] Found matching plugin showroom for URL https://www.showroom-live.com/r/48_KOJIMA_AIKO
[plugins.showroom][debug] Room ID: 270117
[plugins.showroom][error] This stream is restricted
error: No playable streams found on this URL: https://www.showroom-live.com/r/48_KOJIMA_AIKO

- I tried downloading 12 different showroom live streams, but received the same error for all of them.
- I tried changing my IP address using a VPN to a Japan/Hong Kong/Singapore/Germany/USA IP, but the same problem persist.
- Next, I tried to locate the m3u8 address of the showroom stream using stream detector addon (Firefox) and use the .m3u8 address directly in streamlink:

> streamlink --loglevel debug https://hls-css.live.showroom-live.com/live/66f871b4d33f747c738e608bfab98e9fef1aa2d402df70ee64ae4c3fcc1357b1_sourceabr.m3u8 best -o testing.ts

Streamlink was able to work as normal and download successfully:

> D:\>streamlink --loglevel debug https://hls-css.live.showroom-live.com/live/66f871b4d33f747c738e608bfab98e9fef1aa2d402df70ee64ae4c3fcc1357b1_sourceabr.m3u8 best -o testing.ts

> [session][debug] Loading plugin: hls
[cli][debug] OS:         Windows 10
[cli][debug] Python:     3.10.6
[cli][debug] OpenSSL:    OpenSSL 1.1.1n  15 Mar 2022
[cli][debug] Streamlink: 6.7.2
[cli][debug] Dependencies:
[cli][debug]  certifi: 2022.6.15
[cli][debug]  exceptiongroup: 1.2.0
[cli][debug]  isodate: 0.6.1
[cli][debug]  lxml: 4.9.1
[cli][debug]  pycountry: 22.3.5
[cli][debug]  pycryptodome: 3.16.0
[cli][debug]  PySocks: 1.7.1
[cli][debug]  requests: 2.28.2
[cli][debug]  trio: 0.25.0
[cli][debug]  trio-websocket: 0.11.1
[cli][debug]  typing-extensions: 4.5.0
[cli][debug]  urllib3: 1.26.12
[cli][debug]  websocket-client: 1.4.0
[cli][debug] Arguments:
[cli][debug]  url=https://hls-css.live.showroom-live.com/live/66f871b4d33f747c738e608bfab98e9fef1aa2d402df70ee64ae4c3fcc1357b1_sourceabr.m3u8
[cli][debug]  stream=['best']
[cli][debug]  --loglevel=debug
[cli][debug]  --output=testing.ts
[cli][info] Found matching plugin hls for URL https://hls-css.live.showroom-live.com/live/66f871b4d33f747c738e608bfab98e9fef1aa2d402df70ee64ae4c3fcc1357b1_sourceabr.m3u8
[plugins.hls][debug] URL=https://hls-css.live.showroom-live.com/live/66f871b4d33f747c738e608bfab98e9fef1aa2d402df70ee64ae4c3fcc1357b1_sourceabr.m3u8; params={}
[utils.l10n][debug] Language code: en_US
[cli][info] Available streams: live (worst, best)
[cli][info] Opening stream: live (hls)
[cli][info] Writing output to
D:\testing.ts
[cli][debug] Checking file output
[stream.hls][debug] Reloading playlist
[cli][debug] Pre-buffering 8192 bytes
[stream.hls][debug] First Sequence: 8904; Last Sequence: 8906
[stream.hls][debug] Start offset: 0; Duration: None; Start Sequence: 8904; End Sequence: None
[stream.hls][debug] Adding segment 8904 to queue
[stream.hls][debug] Adding segment 8905 to queue
[stream.hls][debug] Adding segment 8906 to queue
[stream.hls][debug] Writing segment 8904 to output
[stream.hls][debug] Segment 8904 complete
[cli][debug] Writing stream to output
[download] Written 538.66 KiB to L:\testing.ts (0s)                                                                    [stream.hls][debug] Writing segment 8905 to output
[stream.hls][debug] Segment 8905 complete
[download] Written 1.17 MiB to L:\testing.ts (0s)                                                                      [stream.hls][debug] Writing segment 8906 to output
[stream.hls][debug] Segment 8906 complete
[download] Written 1.73 MiB to L:\testing.ts (1s)                                                                      [stream.hls][debug] Reloading playlist

I was thinking that this might be a streamlink plugin issue and not Showroom disabling their API, because I tried testing with a Japanese GUI ffmpeg based showroom downloader, called ショールーム録画っち (https://www.skypower.xyz/showroom_rokugatch.html). I was able to download streams successfully by just entering the showroom url.





### Debug log

```text
L:\>streamlink --loglevel debug https://www.showroom-live.com/r/48_KOJIMA_AIKO best -o sample.ts
[session][debug] Loading plugin: showroom
[cli][debug] OS:         Windows 10
[cli][debug] Python:     3.10.6
[cli][debug] OpenSSL:    OpenSSL 1.1.1n  15 Mar 2022
[cli][debug] Streamlink: 6.7.2
[cli][debug] Dependencies:
[cli][debug]  certifi: 2022.6.15
[cli][debug]  exceptiongroup: 1.2.0
[cli][debug]  isodate: 0.6.1
[cli][debug]  lxml: 4.9.1
[cli][debug]  pycountry: 22.3.5
[cli][debug]  pycryptodome: 3.16.0
[cli][debug]  PySocks: 1.7.1
[cli][debug]  requests: 2.28.2
[cli][debug]  trio: 0.25.0
[cli][debug]  trio-websocket: 0.11.1
[cli][debug]  typing-extensions: 4.5.0
[cli][debug]  urllib3: 1.26.12
[cli][debug]  websocket-client: 1.4.0
[cli][debug] Arguments:
[cli][debug]  url=https://www.showroom-live.com/r/48_KOJIMA_AIKO
[cli][debug]  stream=['best']
[cli][debug]  --loglevel=debug
[cli][debug]  --output=sample.ts
[cli][info] Found matching plugin showroom for URL https://www.showroom-live.com/r/48_KOJIMA_AIKO
[plugins.showroom][debug] Room ID: 270117
[plugins.showroom][error] This stream is restricted
error: No playable streams found on this URL: https://www.showroom-live.com/r/48_KOJIMA_AIKO
```

