Repository: streamlink/streamlink

Issue:
plugins.okru: Unable to validate value of key 'author'
### 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)

### Collaboration

- [X] [I will provide feedback should a pull request be opened with a fix for the plugin](https://github.com/streamlink/streamlink/blob/master/CONTRIBUTING.md#pull-request-feedback)

### Streamlink version

Streamlink: 6.8.2 - 6.8.3

### Description

- name: OTB Primorie
  link: https://ok.ru/video/3503444925978
  
- name: 25 Regions
  link: https://ok.ru/videoembed/7930222354061


### Debug log

```text
streamlink https://ok.ru/video/3503444925978 best  --player-continuous-http --player-external-http-port 46203 --player-external-http --loglevel=debug
[session][debug] Loading plugin: okru
[cli][debug] OS:         Linux-6.5.0-41-generic-x86_64-with-glibc2.35
[cli][debug] Python:     3.10.12
[cli][debug] OpenSSL:    OpenSSL 3.0.2 15 Mar 2022
[cli][debug] Streamlink: 6.8.3
[cli][debug] Dependencies:
[cli][debug]  certifi: 2020.12.5
[cli][debug]  isodate: 0.6.1
[cli][debug]  lxml: 4.8.0
[cli][debug]  pycountry: 20.7.3
[cli][debug]  pycryptodome: 3.20.0
[cli][debug]  PySocks: 1.7.1
[cli][debug]  requests: 2.32.3
[cli][debug]  trio: 0.26.0
[cli][debug]  trio-websocket: 0.11.1
[cli][debug]  typing-extensions: 4.11.0
[cli][debug]  urllib3: 1.26.2
[cli][debug]  websocket-client: 1.8.0
[cli][debug]  exceptiongroup: 1.2.1
[cli][debug] Arguments:
[cli][debug]  url=https://ok.ru/video/3503444925978
[cli][debug]  stream=['best']
[cli][debug]  --loglevel=debug
[cli][debug]  --player-continuous-http=True
[cli][debug]  --player-external-http=True
[cli][debug]  --player-external-http-port=46203
[cli][info] Found matching plugin okru for URL https://ok.ru/video/3503444925978
error: Unable to validate result: ValidationError(dict):
  Unable to validate value of key 'author'
  Context(dict):
    Key 'name' not found in {}
```


Discussion:

Patch:
diff --git a/src/streamlink/plugins/okru.py b/src/streamlink/plugins/okru.py
index 78e9587b..2c1dceb8 100644
--- a/src/streamlink/plugins/okru.py
+++ b/src/streamlink/plugins/okru.py
@@ -22,8 +22,14 @@ from streamlink.stream.http import HTTPStream
 log = logging.getLogger(__name__)
 
 
-@pluginmatcher(re.compile(r"https?://(?:www\.)?ok\.ru/"))
-@pluginmatcher(re.compile(r"https?://m(?:obile)?\.ok\.ru/"))
+@pluginmatcher(
+    name="default",
+    pattern=re.compile(r"https?://(?:www\.)?ok\.ru/"),
+)
+@pluginmatcher(
+    name="mobile",
+    pattern=re.compile(r"https?://m(?:obile)?\.ok\.ru/"),
+)
 class OKru(Plugin):
     QUALITY_WEIGHTS = {
         "full": 1080,
@@ -81,8 +87,14 @@ class OKru(Plugin):
         schema_metadata = validate.Schema(
             validate.parse_json(),
             {
-                validate.optional("author"): validate.all({"name": str}, validate.get("name")),
-                validate.optional("movie"): validate.all({"title": str}, validate.get("title")),
+                validate.optional("author"): validate.all({validate.optional("name"): str}, validate.get("name")),
+                validate.optional("movie"): validate.all(
+                    {
+                        validate.optional("id"): str,
+                        validate.optional("title"): str,
+                    },
+                    validate.union_get("id", "title"),
+                ),
                 validate.optional("hlsManifestUrl"): validate.url(),
                 validate.optional("hlsMasterPlaylistUrl"): validate.url(),
                 validate.optional("liveDashManifestUrl"): validate.url(),
@@ -122,7 +134,7 @@ class OKru(Plugin):
         data = schema_metadata.validate(metadata)
 
         self.author = data.get("author")
-        self.title = data.get("movie")
+        self.id, self.title = data.get("movie", (None, None))
 
         for hls_url in data.get("hlsManifestUrl"), data.get("hlsMasterPlaylistUrl"):
             if hls_url is not None:
@@ -137,7 +149,7 @@ class OKru(Plugin):
         }
 
     def _get_streams(self):
-        return self._get_streams_default() if self.matches[0] else self._get_streams_mobile()
+        return self._get_streams_default() if self.matches["default"] else self._get_streams_mobile()
 
 
 __plugin__ = OKru


Test patch:
diff --git a/tests/plugins/test_okru.py b/tests/plugins/test_okru.py
index aa28f2a8..d2f4c278 100644
--- a/tests/plugins/test_okru.py
+++ b/tests/plugins/test_okru.py
@@ -6,10 +6,10 @@ class TestPluginCanHandleUrlOKru(PluginCanHandleUrl):
     __plugin__ = OKru
 
     should_match = [
-        "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",
+        ("default", "https://ok.ru/live/ID"),
+        ("default", "https://ok.ru/video/ID"),
+        ("default", "https://ok.ru/videoembed/ID"),
+        ("default", "https://www.ok.ru/video/ID"),
+        ("mobile", "https://m.ok.ru/video/ID"),
+        ("mobile", "https://mobile.ok.ru/video/ID"),
     ]