We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
plugins.nicolive: Niconico login broken (404)
### 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)
- [ ] [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 niconico function using login is broken. This error is shown:
404 Client Error: Not Found for url: https://account.nicovideo.jp/

The plugin seems to be pointing to https://account.nicovideo.jp/login/redirector which does not work anymore.
Pointing to https://account.nicovideo.jp/login instead should solve the issue.

### Debug log

```text
Used syntax:
streamlink -o "output.ts" https://live.nicovideo.jp/watch/[video-ID] best --niconico-email [mailaddress] --niconico-password [PW]
```


--- 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/plugins/nicolive.py`
Content:
```
1 """
2 $description Japanese live streaming and video hosting social platform.
3 $url live.nicovideo.jp
4 $type live, vod
5 $account Required by some streams
6 $notes Timeshift is supported
7 """
8 
9 import logging
10 import re
11 from threading import Event
12 from urllib.parse import urljoin
13 
14 from streamlink.exceptions import FatalPluginError
15 from streamlink.plugin import Plugin, PluginArgument, PluginArguments, PluginError, pluginmatcher
16 from streamlink.plugin.api import useragents, validate
17 from streamlink.plugin.api.websocket import WebsocketClient
18 from streamlink.stream.hls import HLSStream, HLSStreamReader
19 from streamlink.utils.parse import parse_json
20 from streamlink.utils.times import hours_minutes_seconds
21 from streamlink.utils.url import update_qsd
22 
23 log = logging.getLogger(__name__)
24 
25 
26 class NicoLiveWsClient(WebsocketClient):
27     STREAM_OPENED_TIMEOUT = 6
28 
29     ready: Event
30     opened: Event
31     hls_stream_url: str
32 
33     def __init__(self, *args, **kwargs):
34         super().__init__(*args, **kwargs)
35         self.opened = Event()
36         self.ready = Event()
37 
38     def on_open(self, wsapp):
39         super().on_open(wsapp)
40         self.send_playerversion()
41         self.send_getpermit()
42 
43     def on_message(self, wsapp, data: str):
44         log.debug(f"Received: {data}")
45         message = parse_json(data)
46         msgtype = message.get("type")
47         msgdata = message.get("data", {})
48 
49         if msgtype == "ping":
50             self.send_pong()
51 
52         elif msgtype == "stream" and msgdata.get("protocol") == "hls" and msgdata.get("uri"):
53             self.hls_stream_url = msgdata.get("uri")
54             self.ready.set()
55             if self.opened.wait(self.STREAM_OPENED_TIMEOUT):
56                 log.debug("Stream opened, keeping websocket connection alive")
57             else:
58                 log.info("Closing websocket connection")
59                 self.close()
60 
61         elif msgtype == "disconnect":
62             reason = msgdata.get("reason", "Unknown reason")
63             log.info(f"Received disconnect message: {reason}")
64             self.close()
65 
66     def send_playerversion(self):
67         self.send_json({
68             "type": "startWatching",
69             "data": {
70                 "stream": {
71                     "quality": "abr",
72                     "protocol": "hls",
73                     "latency": "high",
74                     "chasePlay": False
75                 },
76                 "room": {
77                     "protocol": "webSocket",
78                     "commentable": True
79                 },
80                 "reconnect": False
81             }
82         })
83 
84     def send_getpermit(self):
85         self.send_json({
86             "type": "getAkashic",
87             "data": {
88                 "chasePlay": False
89             }
90         })
91 
92     def send_pong(self):
93         self.send_json({"type": "pong"})
94         self.send_json({"type": "keepSeat"})
95 
96 
97 class NicoLiveHLSStreamReader(HLSStreamReader):
98     stream: "NicoLiveHLSStream"
99 
100     def open(self):
101         self.stream.wsclient.opened.set()
102         super().open()
103 
104     def close(self):
105         super().close()
106         self.stream.wsclient.close()
107 
108 
109 class NicoLiveHLSStream(HLSStream):
110     __reader__ = NicoLiveHLSStreamReader
111     wsclient: NicoLiveWsClient
112 
113     def set_wsclient(self, wsclient: NicoLiveWsClient):
114         self.wsclient = wsclient
115 
116 
117 @pluginmatcher(re.compile(
118     r"https?://(?P<domain>live\d*\.nicovideo\.jp)/watch/(lv|co)\d+"
119 ))
120 class NicoLive(Plugin):
121     arguments = PluginArguments(
122         PluginArgument(
123             "email",
124             argument_name="niconico-email",
125             sensitive=True,
126             metavar="EMAIL",
127             help="The email or phone number associated with your Niconico account"
128         ),
129         PluginArgument(
130             "password",
131             argument_name="niconico-password",
132             sensitive=True,
133             metavar="PASSWORD",
134             help="The password of your Niconico account"
135         ),
136         PluginArgument(
137             "user-session",
138             argument_name="niconico-user-session",
139             sensitive=True,
140             metavar="VALUE",
141             help="Value of the user-session token \n(can be used in "
142                  "case you do not want to put your password here)"
143         ),
144         PluginArgument(
145             "purge-credentials",
146             argument_name="niconico-purge-credentials",
147             action="store_true",
148             help="Purge cached Niconico credentials to initiate a new session and reauthenticate."
149         ),
150         PluginArgument(
151             "timeshift-offset",
152             type=hours_minutes_seconds,
153             argument_name="niconico-timeshift-offset",
154             metavar="[HH:]MM:SS",
155             default=None,
156             help="Amount of time to skip from the beginning of a stream. Default is 00:00:00."
157         )
158     )
159 
160     STREAM_READY_TIMEOUT = 6
161     LOGIN_URL = "https://account.nicovideo.jp/login/redirector"
162     LOGIN_URL_PARAMS = {
163         "show_button_twitter": 1,
164         "show_button_facebook": 1,
165         "next_url": "/",
166     }
167 
168     wsclient: NicoLiveWsClient
169 
170     def _get_streams(self):
171         if self.get_option("purge_credentials"):
172             self.clear_cookies()
173             log.info("All credentials were successfully removed")
174 
175         self.session.http.headers.update({
176             "User-Agent": useragents.CHROME,
177         })
178 
179         self.niconico_web_login()
180 
181         wss_api_url = self.get_wss_api_url()
182         if not wss_api_url:
183             log.error(
184                 "Failed to get wss_api_url. "
185                 "Please check if the URL is correct, "
186                 "and make sure your account has access to the video."
187             )
188             return
189 
190         self.wsclient = NicoLiveWsClient(self.session, wss_api_url)
191         self.wsclient.start()
192 
193         hls_stream_url = self._get_hls_stream_url()
194         if not hls_stream_url:
195             return
196 
197         offset = self.get_option("timeshift-offset")
198         if offset and "timeshift" in wss_api_url:
199             hls_stream_url = update_qsd(hls_stream_url, {"start": offset})
200 
201         for quality, stream in NicoLiveHLSStream.parse_variant_playlist(self.session, hls_stream_url).items():
202             stream.set_wsclient(self.wsclient)
203             yield quality, stream
204 
205     def _get_hls_stream_url(self):
206         log.debug(f"Waiting for permit (for at most {self.STREAM_READY_TIMEOUT} seconds)...")
207         if not self.wsclient.ready.wait(self.STREAM_READY_TIMEOUT) or not self.wsclient.is_alive():
208             log.error("Waiting for permit timed out.")
209             self.wsclient.close()
210             return
211 
212         return self.wsclient.hls_stream_url
213 
214     def get_wss_api_url(self):
215         try:
216             data = self.session.http.get(self.url, schema=validate.Schema(
217                 validate.parse_html(),
218                 validate.xml_find(".//script[@id='embedded-data'][@data-props]"),
219                 validate.get("data-props"),
220                 validate.parse_json(),
221                 {"site": {
222                     "relive": {
223                         "webSocketUrl": validate.url(scheme="wss")
224                     },
225                     validate.optional("frontendId"): int
226                 }},
227                 validate.get("site"),
228                 validate.union_get(("relive", "webSocketUrl"), "frontendId")
229             ))
230         except PluginError:
231             return
232 
233         wss_api_url, frontend_id = data
234         if frontend_id is not None:
235             wss_api_url = update_qsd(wss_api_url, {"frontend_id": frontend_id})
236 
237         return wss_api_url
238 
239     def niconico_web_login(self):
240         user_session = self.get_option("user-session")
241         email = self.get_option("email")
242         password = self.get_option("password")
243 
244         if user_session is not None:
245             log.info("Logging in via provided user session cookie")
246             self.session.http.cookies.set(
247                 "user_session",
248                 user_session,
249                 path="/",
250                 domain="nicovideo.jp"
251             )
252             self.save_cookies()
253 
254         elif self.session.http.cookies.get("user_session"):
255             log.info("Logging in via cached user session cookie")
256 
257         elif email is not None and password is not None:
258             log.info("Logging in via provided email and password")
259             root = self.session.http.post(
260                 self.LOGIN_URL,
261                 data={"mail_tel": email, "password": password},
262                 params=self.LOGIN_URL_PARAMS,
263                 schema=validate.Schema(validate.parse_html()))
264 
265             input_with_value = {}
266             for elem in root.xpath(".//input"):
267                 if elem.attrib.get("value"):
268                     input_with_value[elem.attrib.get("name")] = elem.attrib.get("value")
269                 else:
270                     if elem.attrib.get("id") == "oneTimePw":
271                         maxlength = int(elem.attrib.get("maxlength"))
272                         try:
273                             oneTimePw = self.input_ask("Enter the 6 digit number included in email")
274                             if len(oneTimePw) > maxlength:
275                                 log.error("invalid user input")
276                                 return
277                         except FatalPluginError:
278                             return
279                         input_with_value[elem.attrib.get("name")] = oneTimePw
280                     else:
281                         log.debug(f"unknown input: {elem.attrib.get('name')}")
282 
283             root = self.session.http.post(
284                 urljoin("https://account.nicovideo.jp", root.xpath("string(.//form[@action]/@action)")),
285                 data=input_with_value,
286                 schema=validate.Schema(validate.parse_html()))
287             log.debug(f"Cookies: {self.session.http.cookies.get_dict()}")
288             if self.session.http.cookies.get("user_session") is None:
289                 error = root.xpath("string(//div[@class='formError']/div/text())")
290                 log.warning(f"Login failed: {error or 'unknown reason'}")
291             else:
292                 log.info("Logged in.")
293                 self.save_cookies()
294 
295 
296 __plugin__ = NicoLive
297
```
--- 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()

```

diff --git a/src/streamlink/plugins/nicolive.py b/src/streamlink/plugins/nicolive.py
--- a/src/streamlink/plugins/nicolive.py
+++ b/src/streamlink/plugins/nicolive.py
@@ -260,10 +260,16 @@
                 self.LOGIN_URL,
                 data={"mail_tel": email, "password": password},
                 params=self.LOGIN_URL_PARAMS,
-                schema=validate.Schema(validate.parse_html()))
+                schema=validate.Schema(validate.parse_html()),
+            )
+
+            if self.session.http.cookies.get("user_session"):
+                log.info("Logged in.")
+                self.save_cookies()
+                return
 
             input_with_value = {}
-            for elem in root.xpath(".//input"):
+            for elem in root.xpath(".//form[@action]//input"):
                 if elem.attrib.get("value"):
                     input_with_value[elem.attrib.get("name")] = elem.attrib.get("value")
                 else:
@@ -283,7 +289,8 @@
             root = self.session.http.post(
                 urljoin("https://account.nicovideo.jp", root.xpath("string(.//form[@action]/@action)")),
                 data=input_with_value,
-                schema=validate.Schema(validate.parse_html()))
+                schema=validate.Schema(validate.parse_html()),
+            )
             log.debug(f"Cookies: {self.session.http.cookies.get_dict()}")
             if self.session.http.cookies.get("user_session") is None:
                 error = root.xpath("string(//div[@class='formError']/div/text())")

{"golden_diff": "diff --git a/src/streamlink/plugins/nicolive.py b/src/streamlink/plugins/nicolive.py\n--- a/src/streamlink/plugins/nicolive.py\n+++ b/src/streamlink/plugins/nicolive.py\n@@ -260,10 +260,16 @@\n                 self.LOGIN_URL,\n                 data={\"mail_tel\": email, \"password\": password},\n                 params=self.LOGIN_URL_PARAMS,\n-                schema=validate.Schema(validate.parse_html()))\n+                schema=validate.Schema(validate.parse_html()),\n+            )\n+\n+            if self.session.http.cookies.get(\"user_session\"):\n+                log.info(\"Logged in.\")\n+                self.save_cookies()\n+                return\n \n             input_with_value = {}\n-            for elem in root.xpath(\".//input\"):\n+            for elem in root.xpath(\".//form[@action]//input\"):\n                 if elem.attrib.get(\"value\"):\n                     input_with_value[elem.attrib.get(\"name\")] = elem.attrib.get(\"value\")\n                 else:\n@@ -283,7 +289,8 @@\n             root = self.session.http.post(\n                 urljoin(\"https://account.nicovideo.jp\", root.xpath(\"string(.//form[@action]/@action)\")),\n                 data=input_with_value,\n-                schema=validate.Schema(validate.parse_html()))\n+                schema=validate.Schema(validate.parse_html()),\n+            )\n             log.debug(f\"Cookies: {self.session.http.cookies.get_dict()}\")\n             if self.session.http.cookies.get(\"user_session\") is None:\n                 error = root.xpath(\"string(//div[@class='formError']/div/text())\")\n", "issue": "plugins.nicolive: Niconico login broken (404)\n### Checklist\n\n- [X] This is a plugin issue and not a different kind of issue\n- [X] [I have read the contribution guidelines](https://github.com/streamlink/streamlink/blob/master/CONTRIBUTING.md#contributing-to-streamlink)\n- [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)\n- [ ] [I have checked the commit log of the master branch](https://github.com/streamlink/streamlink/commits/master)\n\n### Streamlink version\n\nLatest build from the master branch\n\n### Description\n\nThe niconico function using login is broken. This error is shown:\r\n404 Client Error: Not Found for url: https://account.nicovideo.jp/\r\n\r\nThe plugin seems to be pointing to https://account.nicovideo.jp/login/redirector which does not work anymore.\r\nPointing to https://account.nicovideo.jp/login instead should solve the issue.\n\n### Debug log\n\n```text\nUsed syntax:\r\nstreamlink -o \"output.ts\" https://live.nicovideo.jp/watch/[video-ID] best --niconico-email [mailaddress] --niconico-password [PW]\n```\n\n", "before_files": [{"content": "\"\"\"\n$description Japanese live streaming and video hosting social platform.\n$url live.nicovideo.jp\n$type live, vod\n$account Required by some streams\n$notes Timeshift is supported\n\"\"\"\n\nimport logging\nimport re\nfrom threading import Event\nfrom urllib.parse import urljoin\n\nfrom streamlink.exceptions import FatalPluginError\nfrom streamlink.plugin import Plugin, PluginArgument, PluginArguments, PluginError, pluginmatcher\nfrom streamlink.plugin.api import useragents, validate\nfrom streamlink.plugin.api.websocket import WebsocketClient\nfrom streamlink.stream.hls import HLSStream, HLSStreamReader\nfrom streamlink.utils.parse import parse_json\nfrom streamlink.utils.times import hours_minutes_seconds\nfrom streamlink.utils.url import update_qsd\n\nlog = logging.getLogger(__name__)\n\n\nclass NicoLiveWsClient(WebsocketClient):\n    STREAM_OPENED_TIMEOUT = 6\n\n    ready: Event\n    opened: Event\n    hls_stream_url: str\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.opened = Event()\n        self.ready = Event()\n\n    def on_open(self, wsapp):\n        super().on_open(wsapp)\n        self.send_playerversion()\n        self.send_getpermit()\n\n    def on_message(self, wsapp, data: str):\n        log.debug(f\"Received: {data}\")\n        message = parse_json(data)\n        msgtype = message.get(\"type\")\n        msgdata = message.get(\"data\", {})\n\n        if msgtype == \"ping\":\n            self.send_pong()\n\n        elif msgtype == \"stream\" and msgdata.get(\"protocol\") == \"hls\" and msgdata.get(\"uri\"):\n            self.hls_stream_url = msgdata.get(\"uri\")\n            self.ready.set()\n            if self.opened.wait(self.STREAM_OPENED_TIMEOUT):\n                log.debug(\"Stream opened, keeping websocket connection alive\")\n            else:\n                log.info(\"Closing websocket connection\")\n                self.close()\n\n        elif msgtype == \"disconnect\":\n            reason = msgdata.get(\"reason\", \"Unknown reason\")\n            log.info(f\"Received disconnect message: {reason}\")\n            self.close()\n\n    def send_playerversion(self):\n        self.send_json({\n            \"type\": \"startWatching\",\n            \"data\": {\n                \"stream\": {\n                    \"quality\": \"abr\",\n                    \"protocol\": \"hls\",\n                    \"latency\": \"high\",\n                    \"chasePlay\": False\n                },\n                \"room\": {\n                    \"protocol\": \"webSocket\",\n                    \"commentable\": True\n                },\n                \"reconnect\": False\n            }\n        })\n\n    def send_getpermit(self):\n        self.send_json({\n            \"type\": \"getAkashic\",\n            \"data\": {\n                \"chasePlay\": False\n            }\n        })\n\n    def send_pong(self):\n        self.send_json({\"type\": \"pong\"})\n        self.send_json({\"type\": \"keepSeat\"})\n\n\nclass NicoLiveHLSStreamReader(HLSStreamReader):\n    stream: \"NicoLiveHLSStream\"\n\n    def open(self):\n        self.stream.wsclient.opened.set()\n        super().open()\n\n    def close(self):\n        super().close()\n        self.stream.wsclient.close()\n\n\nclass NicoLiveHLSStream(HLSStream):\n    __reader__ = NicoLiveHLSStreamReader\n    wsclient: NicoLiveWsClient\n\n    def set_wsclient(self, wsclient: NicoLiveWsClient):\n        self.wsclient = wsclient\n\n\n@pluginmatcher(re.compile(\n    r\"https?://(?P<domain>live\\d*\\.nicovideo\\.jp)/watch/(lv|co)\\d+\"\n))\nclass NicoLive(Plugin):\n    arguments = PluginArguments(\n        PluginArgument(\n            \"email\",\n            argument_name=\"niconico-email\",\n            sensitive=True,\n            metavar=\"EMAIL\",\n            help=\"The email or phone number associated with your Niconico account\"\n        ),\n        PluginArgument(\n            \"password\",\n            argument_name=\"niconico-password\",\n            sensitive=True,\n            metavar=\"PASSWORD\",\n            help=\"The password of your Niconico account\"\n        ),\n        PluginArgument(\n            \"user-session\",\n            argument_name=\"niconico-user-session\",\n            sensitive=True,\n            metavar=\"VALUE\",\n            help=\"Value of the user-session token \\n(can be used in \"\n                 \"case you do not want to put your password here)\"\n        ),\n        PluginArgument(\n            \"purge-credentials\",\n            argument_name=\"niconico-purge-credentials\",\n            action=\"store_true\",\n            help=\"Purge cached Niconico credentials to initiate a new session and reauthenticate.\"\n        ),\n        PluginArgument(\n            \"timeshift-offset\",\n            type=hours_minutes_seconds,\n            argument_name=\"niconico-timeshift-offset\",\n            metavar=\"[HH:]MM:SS\",\n            default=None,\n            help=\"Amount of time to skip from the beginning of a stream. Default is 00:00:00.\"\n        )\n    )\n\n    STREAM_READY_TIMEOUT = 6\n    LOGIN_URL = \"https://account.nicovideo.jp/login/redirector\"\n    LOGIN_URL_PARAMS = {\n        \"show_button_twitter\": 1,\n        \"show_button_facebook\": 1,\n        \"next_url\": \"/\",\n    }\n\n    wsclient: NicoLiveWsClient\n\n    def _get_streams(self):\n        if self.get_option(\"purge_credentials\"):\n            self.clear_cookies()\n            log.info(\"All credentials were successfully removed\")\n\n        self.session.http.headers.update({\n            \"User-Agent\": useragents.CHROME,\n        })\n\n        self.niconico_web_login()\n\n        wss_api_url = self.get_wss_api_url()\n        if not wss_api_url:\n            log.error(\n                \"Failed to get wss_api_url. \"\n                \"Please check if the URL is correct, \"\n                \"and make sure your account has access to the video.\"\n            )\n            return\n\n        self.wsclient = NicoLiveWsClient(self.session, wss_api_url)\n        self.wsclient.start()\n\n        hls_stream_url = self._get_hls_stream_url()\n        if not hls_stream_url:\n            return\n\n        offset = self.get_option(\"timeshift-offset\")\n        if offset and \"timeshift\" in wss_api_url:\n            hls_stream_url = update_qsd(hls_stream_url, {\"start\": offset})\n\n        for quality, stream in NicoLiveHLSStream.parse_variant_playlist(self.session, hls_stream_url).items():\n            stream.set_wsclient(self.wsclient)\n            yield quality, stream\n\n    def _get_hls_stream_url(self):\n        log.debug(f\"Waiting for permit (for at most {self.STREAM_READY_TIMEOUT} seconds)...\")\n        if not self.wsclient.ready.wait(self.STREAM_READY_TIMEOUT) or not self.wsclient.is_alive():\n            log.error(\"Waiting for permit timed out.\")\n            self.wsclient.close()\n            return\n\n        return self.wsclient.hls_stream_url\n\n    def get_wss_api_url(self):\n        try:\n            data = self.session.http.get(self.url, schema=validate.Schema(\n                validate.parse_html(),\n                validate.xml_find(\".//script[@id='embedded-data'][@data-props]\"),\n                validate.get(\"data-props\"),\n                validate.parse_json(),\n                {\"site\": {\n                    \"relive\": {\n                        \"webSocketUrl\": validate.url(scheme=\"wss\")\n                    },\n                    validate.optional(\"frontendId\"): int\n                }},\n                validate.get(\"site\"),\n                validate.union_get((\"relive\", \"webSocketUrl\"), \"frontendId\")\n            ))\n        except PluginError:\n            return\n\n        wss_api_url, frontend_id = data\n        if frontend_id is not None:\n            wss_api_url = update_qsd(wss_api_url, {\"frontend_id\": frontend_id})\n\n        return wss_api_url\n\n    def niconico_web_login(self):\n        user_session = self.get_option(\"user-session\")\n        email = self.get_option(\"email\")\n        password = self.get_option(\"password\")\n\n        if user_session is not None:\n            log.info(\"Logging in via provided user session cookie\")\n            self.session.http.cookies.set(\n                \"user_session\",\n                user_session,\n                path=\"/\",\n                domain=\"nicovideo.jp\"\n            )\n            self.save_cookies()\n\n        elif self.session.http.cookies.get(\"user_session\"):\n            log.info(\"Logging in via cached user session cookie\")\n\n        elif email is not None and password is not None:\n            log.info(\"Logging in via provided email and password\")\n            root = self.session.http.post(\n                self.LOGIN_URL,\n                data={\"mail_tel\": email, \"password\": password},\n                params=self.LOGIN_URL_PARAMS,\n                schema=validate.Schema(validate.parse_html()))\n\n            input_with_value = {}\n            for elem in root.xpath(\".//input\"):\n                if elem.attrib.get(\"value\"):\n                    input_with_value[elem.attrib.get(\"name\")] = elem.attrib.get(\"value\")\n                else:\n                    if elem.attrib.get(\"id\") == \"oneTimePw\":\n                        maxlength = int(elem.attrib.get(\"maxlength\"))\n                        try:\n                            oneTimePw = self.input_ask(\"Enter the 6 digit number included in email\")\n                            if len(oneTimePw) > maxlength:\n                                log.error(\"invalid user input\")\n                                return\n                        except FatalPluginError:\n                            return\n                        input_with_value[elem.attrib.get(\"name\")] = oneTimePw\n                    else:\n                        log.debug(f\"unknown input: {elem.attrib.get('name')}\")\n\n            root = self.session.http.post(\n                urljoin(\"https://account.nicovideo.jp\", root.xpath(\"string(.//form[@action]/@action)\")),\n                data=input_with_value,\n                schema=validate.Schema(validate.parse_html()))\n            log.debug(f\"Cookies: {self.session.http.cookies.get_dict()}\")\n            if self.session.http.cookies.get(\"user_session\") is None:\n                error = root.xpath(\"string(//div[@class='formError']/div/text())\")\n                log.warning(f\"Login failed: {error or 'unknown reason'}\")\n            else:\n                log.info(\"Logged in.\")\n                self.save_cookies()\n\n\n__plugin__ = NicoLive\n", "path": "src/streamlink/plugins/nicolive.py"}], "after_files": [{"content": "\"\"\"\n$description Japanese live streaming and video hosting social platform.\n$url live.nicovideo.jp\n$type live, vod\n$account Required by some streams\n$notes Timeshift is supported\n\"\"\"\n\nimport logging\nimport re\nfrom threading import Event\nfrom urllib.parse import urljoin\n\nfrom streamlink.exceptions import FatalPluginError\nfrom streamlink.plugin import Plugin, PluginArgument, PluginArguments, PluginError, pluginmatcher\nfrom streamlink.plugin.api import useragents, validate\nfrom streamlink.plugin.api.websocket import WebsocketClient\nfrom streamlink.stream.hls import HLSStream, HLSStreamReader\nfrom streamlink.utils.parse import parse_json\nfrom streamlink.utils.times import hours_minutes_seconds\nfrom streamlink.utils.url import update_qsd\n\nlog = logging.getLogger(__name__)\n\n\nclass NicoLiveWsClient(WebsocketClient):\n    STREAM_OPENED_TIMEOUT = 6\n\n    ready: Event\n    opened: Event\n    hls_stream_url: str\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.opened = Event()\n        self.ready = Event()\n\n    def on_open(self, wsapp):\n        super().on_open(wsapp)\n        self.send_playerversion()\n        self.send_getpermit()\n\n    def on_message(self, wsapp, data: str):\n        log.debug(f\"Received: {data}\")\n        message = parse_json(data)\n        msgtype = message.get(\"type\")\n        msgdata = message.get(\"data\", {})\n\n        if msgtype == \"ping\":\n            self.send_pong()\n\n        elif msgtype == \"stream\" and msgdata.get(\"protocol\") == \"hls\" and msgdata.get(\"uri\"):\n            self.hls_stream_url = msgdata.get(\"uri\")\n            self.ready.set()\n            if self.opened.wait(self.STREAM_OPENED_TIMEOUT):\n                log.debug(\"Stream opened, keeping websocket connection alive\")\n            else:\n                log.info(\"Closing websocket connection\")\n                self.close()\n\n        elif msgtype == \"disconnect\":\n            reason = msgdata.get(\"reason\", \"Unknown reason\")\n            log.info(f\"Received disconnect message: {reason}\")\n            self.close()\n\n    def send_playerversion(self):\n        self.send_json({\n            \"type\": \"startWatching\",\n            \"data\": {\n                \"stream\": {\n                    \"quality\": \"abr\",\n                    \"protocol\": \"hls\",\n                    \"latency\": \"high\",\n                    \"chasePlay\": False\n                },\n                \"room\": {\n                    \"protocol\": \"webSocket\",\n                    \"commentable\": True\n                },\n                \"reconnect\": False\n            }\n        })\n\n    def send_getpermit(self):\n        self.send_json({\n            \"type\": \"getAkashic\",\n            \"data\": {\n                \"chasePlay\": False\n            }\n        })\n\n    def send_pong(self):\n        self.send_json({\"type\": \"pong\"})\n        self.send_json({\"type\": \"keepSeat\"})\n\n\nclass NicoLiveHLSStreamReader(HLSStreamReader):\n    stream: \"NicoLiveHLSStream\"\n\n    def open(self):\n        self.stream.wsclient.opened.set()\n        super().open()\n\n    def close(self):\n        super().close()\n        self.stream.wsclient.close()\n\n\nclass NicoLiveHLSStream(HLSStream):\n    __reader__ = NicoLiveHLSStreamReader\n    wsclient: NicoLiveWsClient\n\n    def set_wsclient(self, wsclient: NicoLiveWsClient):\n        self.wsclient = wsclient\n\n\n@pluginmatcher(re.compile(\n    r\"https?://(?P<domain>live\\d*\\.nicovideo\\.jp)/watch/(lv|co)\\d+\"\n))\nclass NicoLive(Plugin):\n    arguments = PluginArguments(\n        PluginArgument(\n            \"email\",\n            argument_name=\"niconico-email\",\n            sensitive=True,\n            metavar=\"EMAIL\",\n            help=\"The email or phone number associated with your Niconico account\"\n        ),\n        PluginArgument(\n            \"password\",\n            argument_name=\"niconico-password\",\n            sensitive=True,\n            metavar=\"PASSWORD\",\n            help=\"The password of your Niconico account\"\n        ),\n        PluginArgument(\n            \"user-session\",\n            argument_name=\"niconico-user-session\",\n            sensitive=True,\n            metavar=\"VALUE\",\n            help=\"Value of the user-session token \\n(can be used in \"\n                 \"case you do not want to put your password here)\"\n        ),\n        PluginArgument(\n            \"purge-credentials\",\n            argument_name=\"niconico-purge-credentials\",\n            action=\"store_true\",\n            help=\"Purge cached Niconico credentials to initiate a new session and reauthenticate.\"\n        ),\n        PluginArgument(\n            \"timeshift-offset\",\n            type=hours_minutes_seconds,\n            argument_name=\"niconico-timeshift-offset\",\n            metavar=\"[HH:]MM:SS\",\n            default=None,\n            help=\"Amount of time to skip from the beginning of a stream. Default is 00:00:00.\"\n        )\n    )\n\n    STREAM_READY_TIMEOUT = 6\n    LOGIN_URL = \"https://account.nicovideo.jp/login/redirector\"\n    LOGIN_URL_PARAMS = {\n        \"show_button_twitter\": 1,\n        \"show_button_facebook\": 1,\n        \"next_url\": \"/\",\n    }\n\n    wsclient: NicoLiveWsClient\n\n    def _get_streams(self):\n        if self.get_option(\"purge_credentials\"):\n            self.clear_cookies()\n            log.info(\"All credentials were successfully removed\")\n\n        self.session.http.headers.update({\n            \"User-Agent\": useragents.CHROME,\n        })\n\n        self.niconico_web_login()\n\n        wss_api_url = self.get_wss_api_url()\n        if not wss_api_url:\n            log.error(\n                \"Failed to get wss_api_url. \"\n                \"Please check if the URL is correct, \"\n                \"and make sure your account has access to the video.\"\n            )\n            return\n\n        self.wsclient = NicoLiveWsClient(self.session, wss_api_url)\n        self.wsclient.start()\n\n        hls_stream_url = self._get_hls_stream_url()\n        if not hls_stream_url:\n            return\n\n        offset = self.get_option(\"timeshift-offset\")\n        if offset and \"timeshift\" in wss_api_url:\n            hls_stream_url = update_qsd(hls_stream_url, {\"start\": offset})\n\n        for quality, stream in NicoLiveHLSStream.parse_variant_playlist(self.session, hls_stream_url).items():\n            stream.set_wsclient(self.wsclient)\n            yield quality, stream\n\n    def _get_hls_stream_url(self):\n        log.debug(f\"Waiting for permit (for at most {self.STREAM_READY_TIMEOUT} seconds)...\")\n        if not self.wsclient.ready.wait(self.STREAM_READY_TIMEOUT) or not self.wsclient.is_alive():\n            log.error(\"Waiting for permit timed out.\")\n            self.wsclient.close()\n            return\n\n        return self.wsclient.hls_stream_url\n\n    def get_wss_api_url(self):\n        try:\n            data = self.session.http.get(self.url, schema=validate.Schema(\n                validate.parse_html(),\n                validate.xml_find(\".//script[@id='embedded-data'][@data-props]\"),\n                validate.get(\"data-props\"),\n                validate.parse_json(),\n                {\"site\": {\n                    \"relive\": {\n                        \"webSocketUrl\": validate.url(scheme=\"wss\")\n                    },\n                    validate.optional(\"frontendId\"): int\n                }},\n                validate.get(\"site\"),\n                validate.union_get((\"relive\", \"webSocketUrl\"), \"frontendId\")\n            ))\n        except PluginError:\n            return\n\n        wss_api_url, frontend_id = data\n        if frontend_id is not None:\n            wss_api_url = update_qsd(wss_api_url, {\"frontend_id\": frontend_id})\n\n        return wss_api_url\n\n    def niconico_web_login(self):\n        user_session = self.get_option(\"user-session\")\n        email = self.get_option(\"email\")\n        password = self.get_option(\"password\")\n\n        if user_session is not None:\n            log.info(\"Logging in via provided user session cookie\")\n            self.session.http.cookies.set(\n                \"user_session\",\n                user_session,\n                path=\"/\",\n                domain=\"nicovideo.jp\"\n            )\n            self.save_cookies()\n\n        elif self.session.http.cookies.get(\"user_session\"):\n            log.info(\"Logging in via cached user session cookie\")\n\n        elif email is not None and password is not None:\n            log.info(\"Logging in via provided email and password\")\n            root = self.session.http.post(\n                self.LOGIN_URL,\n                data={\"mail_tel\": email, \"password\": password},\n                params=self.LOGIN_URL_PARAMS,\n                schema=validate.Schema(validate.parse_html()),\n            )\n\n            if self.session.http.cookies.get(\"user_session\"):\n                log.info(\"Logged in.\")\n                self.save_cookies()\n                return\n\n            input_with_value = {}\n            for elem in root.xpath(\".//form[@action]//input\"):\n                if elem.attrib.get(\"value\"):\n                    input_with_value[elem.attrib.get(\"name\")] = elem.attrib.get(\"value\")\n                else:\n                    if elem.attrib.get(\"id\") == \"oneTimePw\":\n                        maxlength = int(elem.attrib.get(\"maxlength\"))\n                        try:\n                            oneTimePw = self.input_ask(\"Enter the 6 digit number included in email\")\n                            if len(oneTimePw) > maxlength:\n                                log.error(\"invalid user input\")\n                                return\n                        except FatalPluginError:\n                            return\n                        input_with_value[elem.attrib.get(\"name\")] = oneTimePw\n                    else:\n                        log.debug(f\"unknown input: {elem.attrib.get('name')}\")\n\n            root = self.session.http.post(\n                urljoin(\"https://account.nicovideo.jp\", root.xpath(\"string(.//form[@action]/@action)\")),\n                data=input_with_value,\n                schema=validate.Schema(validate.parse_html()),\n            )\n            log.debug(f\"Cookies: {self.session.http.cookies.get_dict()}\")\n            if self.session.http.cookies.get(\"user_session\") is None:\n                error = root.xpath(\"string(//div[@class='formError']/div/text())\")\n                log.warning(f\"Login failed: {error or 'unknown reason'}\")\n            else:\n                log.info(\"Logged in.\")\n                self.save_cookies()\n\n\n__plugin__ = NicoLive\n", "path": "src/streamlink/plugins/nicolive.py"}]}