Repository: streamlink/streamlink

Issue:
mobile.ok.ru
### Checklist

- [X] This is a plugin 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%22plugin+request%22)

### Description

it is the mobile version of ok.ru
cuz it is banned here in Egypt

### Input URLs

mobile.ok.ru



Discussion:
The video url is the one with
data-objid="xxx"

while xxx is the videoid 
You just need to remove "m." or "mobile." from the URL

e.g

```
~$ streamlink 'https://m.ok.ru/live/1366902775399'
error: No plugin can handle URL: https://m.ok.ru/live/1366902775399
~$ streamlink 'https://mobile.ok.ru/live/1366902775399'
error: No plugin can handle URL: https://mobile.ok.ru/live/1366902775399

~$ streamlink 'https://ok.ru/live/1366902775399'     
[cli][info] Found matching plugin okru for URL https://ok.ru/live/1366902775399
Available streams: 240p (worst), 360p, 480p, 720p (best)
```
@bastimeyer I tried updating the regex to allow the "m." or "mobile." urls to load with the okru plugin but it seems the extraction code needs to be updated for the mobile pages. Maybe the plugin can just remove the "m." or "mobile." from the URL?

```
~$ streamlink 'https://mobile.ok.ru/live/1366902775399'   
[cli][info] Found matching plugin okru for URL https://mobile.ok.ru/live/1366902775399
[plugins.okru][error] Could not find metadata
error: No playable streams found on this URL: https://mobile.ok.ru/live/1366902775399
~$ streamlink 'https://m.ok.ru/live/1366902775399'    
[cli][info] Found matching plugin okru for URL https://m.ok.ru/live/1366902775399
[plugins.okru][error] Could not find metadata
error: No playable streams found on this URL: https://m.ok.ru/live/1366902775399
~$ 
```

> but it seems the extraction code needs to be updated for the mobile pages
> You just need to remove "m." or "mobile." from the URL

Take a look at the YouTube plugin. There's logic for modifying the URL before the page gets accessed.
https://github.com/streamlink/streamlink/blob/b7b0353b5168a713f4f265ceec432c734aee94cf/src/streamlink/plugins/youtube.py#L79-L91

That should do the trick. If you can confirm these changes, then I will submit a PR.

```diff
diff --git a/src/streamlink/plugins/okru.py b/src/streamlink/plugins/okru.py
index bcb1f699..39c90bd1 100644
--- a/src/streamlink/plugins/okru.py
+++ b/src/streamlink/plugins/okru.py
@@ -1,12 +1,12 @@
 """
-$description Russian live streaming and video hosting social platform.
+$description Russian live-streaming and video hosting social platform.
 $url ok.ru
 $type live, vod
 """
 
 import logging
 import re
-from urllib.parse import unquote
+from urllib.parse import unquote, urlparse, urlunparse
 
 from streamlink.plugin import Plugin, PluginError, pluginmatcher
 from streamlink.plugin.api import validate
@@ -18,7 +18,7 @@ log = logging.getLogger(__name__)
 
 
 @pluginmatcher(re.compile(
-    r'https?://(?:www\.)?ok\.ru/'
+    r"https?://(?:\w+\.)?ok\.ru/"
 ))
 class OKru(Plugin):
     QUALITY_WEIGHTS = {
@@ -42,6 +42,11 @@ class OKru(Plugin):
 
         return super().stream_weight(key)
 
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        parsed = urlparse(self.url)
+        self.url = urlunparse(parsed._replace(netloc=re.sub(r"^m(obile)?\.", "", parsed.netloc)))
+
     def _get_streams(self):
         schema_metadata = validate.Schema(
             validate.parse_json(),
diff --git a/tests/plugins/test_okru.py b/tests/plugins/test_okru.py
index f6aa00c1..acf47987 100644
--- a/tests/plugins/test_okru.py
+++ b/tests/plugins/test_okru.py
@@ -1,3 +1,5 @@
+import pytest
+
 from streamlink.plugins.okru import OKru
 from tests.plugins import PluginCanHandleUrl
 
@@ -6,8 +8,20 @@ class TestPluginCanHandleUrlOKru(PluginCanHandleUrl):
     __plugin__ = OKru
 
     should_match = [
-        'https://ok.ru/live/12345',
-        'http://ok.ru/live/12345',
-        'http://www.ok.ru/live/12345',
-        'https://ok.ru/video/266205792931',
+        "http://ok.ru/live/12345",
+        "https://ok.ru/live/12345",
+        "https://m.ok.ru/live/12345",
+        "https://mobile.ok.ru/live/12345",
+        "https://www.ok.ru/live/12345",
+        "https://ok.ru/video/266205792931",
     ]
+
+
+class TestOKru:
+    @pytest.mark.parametrize("url,expected", [
+        ("https://m.ok.ru/live/12345", "https://ok.ru/live/12345"),
+        ("https://mobile.ok.ru/live/12345", "https://ok.ru/live/12345"),
+    ])
+    def test_url_mobile(self, url, expected):
+        plugin = OKru(url)
+        assert plugin.url == expected
```
Thankyou @bastimeyer. It worked perfectly.

```
~$ streamlink 'https://mobile.ok.ru/live/1366902775399' best
[cli][info] Found matching plugin okru for URL https://mobile.ok.ru/live/1366902775399
[cli][info] Available streams: 240p (worst), 360p, 480p, 720p (best)
[cli][info] Opening stream: 720p (hls)
[cli][info] Starting player: /opt/homebrew/bin/VLC
^[[A[cli][info] Player closed
[cli][info] Stream ended
[cli][info] Closing currently open stream...

                                                                                                                                                                                                                                         
~$ streamlink 'https://m.ok.ru/live/1366902775399' best 
[cli][info] Found matching plugin okru for URL https://m.ok.ru/live/1366902775399
[cli][info] Available streams: 240p (worst), 360p, 480p, 720p (best)
[cli][info] Opening stream: 720p (hls)
[cli][info] Starting player: /opt/homebrew/bin/VLC
^[[A[cli][info] Player closed
[cli][info] Stream ended
[cli][info] Closing currently open stream...

~$ streamlink 'https://ok.ru/live/1366902775399' best 
[cli][info] Found matching plugin okru for URL https://ok.ru/live/1366902775399
[cli][info] Available streams: 240p (worst), 360p, 480p, 720p (best)
[cli][info] Opening stream: 720p (hls)
[cli][info] Starting player: /opt/homebrew/bin/VLC
^[[A[cli][info] Player closed
[cli][info] Stream ended
[cli][info] Closing currently open stream...

```
I don't think the proposed solution will do what the OP was asking for.  I don't know how Egypt is enabling its blocking - whether via transparent proxy or DNS, but either way it seems likely that the proposed solution will still be blocked for the OP.
True. I didn't check OP's comment... :/

I only have that one link posted in https://github.com/streamlink/streamlink/issues/4682#issuecomment-1229226006 though. The site requires having an account, and I don't want to create one.

The HLS URL, video ID and video title can be found in the JSON data of this XPath query `string(.//a[@data-video][1]/@data-video)`, so implementing the mobile site is trivial if the same layout gets used everywhere else.

Resolving patch:
diff --git a/src/streamlink/plugins/okru.py b/src/streamlink/plugins/okru.py
--- a/src/streamlink/plugins/okru.py
+++ b/src/streamlink/plugins/okru.py
@@ -1,14 +1,15 @@
 """
 $description Russian live-streaming and video hosting social platform.
 $url ok.ru
+$url mobile.ok.ru
 $type live, vod
 """
 
 import logging
 import re
-from urllib.parse import unquote, urlparse, urlunparse
+from urllib.parse import unquote, urlparse
 
-from streamlink.plugin import Plugin, PluginError, pluginmatcher
+from streamlink.plugin import Plugin, pluginmatcher
 from streamlink.plugin.api import validate
 from streamlink.stream.dash import DASHStream
 from streamlink.stream.hls import HLSStream
@@ -17,9 +18,8 @@
 log = logging.getLogger(__name__)
 
 
-@pluginmatcher(re.compile(
-    r"https?://(?:\w+\.)?ok\.ru/"
-))
+@pluginmatcher(re.compile(r"https?://(?:www\.)?ok\.ru/"))
+@pluginmatcher(re.compile(r"https?://m(?:obile)?\.ok\.ru/"))
 class OKru(Plugin):
     QUALITY_WEIGHTS = {
         "full": 1080,
@@ -42,12 +42,38 @@ def stream_weight(cls, key):
 
         return super().stream_weight(key)
 
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-        parsed = urlparse(self.url)
-        self.url = urlunparse(parsed._replace(netloc=re.sub(r"^m(obile)?\.", "", parsed.netloc)))
+    def _get_streams_mobile(self):
+        data = self.session.http.get(self.url, schema=validate.Schema(
+            validate.parse_html(),
+            validate.xml_find(".//a[@data-video]"),
+            validate.get("data-video"),
+            validate.none_or_all(
+                str,
+                validate.parse_json(),
+                {
+                    "videoName": str,
+                    "videoSrc": validate.url(),
+                    "movieId": str,
+                },
+                validate.union_get("movieId", "videoName", "videoSrc"),
+            ),
+        ))
+        if not data:
+            return
 
-    def _get_streams(self):
+        self.id, self.title, url = data
+
+        stream_url = self.session.http.head(url).headers.get("Location")
+        if not stream_url:
+            return
+
+        return (
+            HLSStream.parse_variant_playlist(self.session, stream_url)
+            if urlparse(stream_url).path.endswith(".m3u8") else
+            {"vod": HTTPStream(self.session, stream_url)}
+        )
+
+    def _get_streams_default(self):
         schema_metadata = validate.Schema(
             validate.parse_json(),
             {
@@ -59,32 +85,28 @@ def _get_streams(self):
                 validate.optional("videos"): [validate.all(
                     {
                         "name": str,
-                        "url": validate.url()
+                        "url": validate.url(),
                     },
-                    validate.union_get("name", "url")
-                )]
-            }
+                    validate.union_get("name", "url"),
+                )],
+            },
         )
 
-        try:
-            metadata, metadata_url = self.session.http.get(self.url, schema=validate.Schema(
-                validate.parse_html(),
-                validate.xml_find(".//*[@data-options]"),
-                validate.get("data-options"),
-                validate.parse_json(),
-                {"flashvars": {
-                    validate.optional("metadata"): str,
-                    validate.optional("metadataUrl"): validate.all(
-                        validate.transform(unquote),
-                        validate.url()
-                    )
-                }},
-                validate.get("flashvars"),
-                validate.union_get("metadata", "metadataUrl")
-            ))
-        except PluginError:
-            log.error("Could not find metadata")
-            return
+        metadata, metadata_url = self.session.http.get(self.url, schema=validate.Schema(
+            validate.parse_html(),
+            validate.xml_find(".//*[@data-options]"),
+            validate.get("data-options"),
+            validate.parse_json(),
+            {"flashvars": {
+                validate.optional("metadata"): str,
+                validate.optional("metadataUrl"): validate.all(
+                    validate.transform(unquote),
+                    validate.url(),
+                ),
+            }},
+            validate.get("flashvars"),
+            validate.union_get("metadata", "metadataUrl"),
+        ))
 
         self.session.http.headers.update({"Referer": self.url})
 
@@ -93,11 +115,7 @@ def _get_streams(self):
 
         log.trace(f"{metadata!r}")
 
-        try:
-            data = schema_metadata.validate(metadata)
-        except PluginError:
-            log.error("Could not parse metadata")
-            return
+        data = schema_metadata.validate(metadata)
 
         self.author = data.get("author")
         self.title = data.get("movie")
@@ -114,5 +132,8 @@ def _get_streams(self):
             for name, url in data.get("videos", [])
         }
 
+    def _get_streams(self):
+        return self._get_streams_default() if self.matches[0] else self._get_streams_mobile()
+
 
 __plugin__ = OKru


Test patch:
diff --git a/tests/plugins/test_okru.py b/tests/plugins/test_okru.py
--- a/tests/plugins/test_okru.py
+++ b/tests/plugins/test_okru.py
@@ -1,5 +1,3 @@
-import pytest
-
 from streamlink.plugins.okru import OKru
 from tests.plugins import PluginCanHandleUrl
 
@@ -15,13 +13,3 @@ class TestPluginCanHandleUrlOKru(PluginCanHandleUrl):
         "https://www.ok.ru/live/12345",
         "https://ok.ru/video/266205792931",
     ]
-
-
-class TestOKru:
-    @pytest.mark.parametrize("url,expected", [
-        ("https://m.ok.ru/live/12345", "https://ok.ru/live/12345"),
-        ("https://mobile.ok.ru/live/12345", "https://ok.ru/live/12345"),
-    ])
-    def test_url_mobile(self, url, expected):
-        plugin = OKru(url)
-        assert plugin.url == expected