Repository: streamlink/streamlink
Pull request #1451 (2018-01-24 22:46:09)

Add ok.ru/live plugin
This PR adds a plugin for ok.ru live videos (#1404)

codecov[bot]: # [Codecov](https://codecov.io/gh/streamlink/streamlink/pull/1451?src=pr&el=h1) Report
> Merging [#1451](https://codecov.io/gh/streamlink/streamlink/pull/1451?src=pr&el=desc) into [master](https://codecov.io/gh/streamlink/streamlink/commit/b839cfd76b63eec3646a58ca19d7be560c422592?src=pr&el=desc) will **decrease** coverage by `0.01%`.
> The diff coverage is `n/a`.


```diff
@@            Coverage Diff            @@
##           master   #1451      +/-   ##
=========================================
- Coverage   50.31%   50.3%   -0.02%     
=========================================
  Files         107     107              
  Lines        7660    7660              
=========================================
- Hits         3854    3853       -1     
- Misses       3806    3807       +1
```

gravyboat: I'm not okay with adding an additional dependency (bs4) to the project for a single site, do they not have some kind of API or anything that can be used? We can check with @beardypig and @bastimeyer as well but I highly doubt they will want to add such a dependency either.
atykhyy: Not in their [public API](https://apiok.ru/dev/methods/rest/video/), and their mobile app is obfuscated. It's possible, I suppose, to fish the HLS playlist URL out of the raw HTML with a regular expression, as many other plugins do, but HTML is structured data, why not use it as such? Navigating HTML with XPath or equivalent results in more readable code, and is less brittle to minor textual changes in the HTML than regular expressions, where e.g. a comment or swapped tag attributes can easily ruin one's day.

Patch:
diff --git a/src/streamlink/plugins/ok_live.py b/src/streamlink/plugins/ok_live.py
new file mode 100644
index 00000000..32edad59
--- /dev/null
+++ b/src/streamlink/plugins/ok_live.py
@@ -0,0 +1,41 @@
+import urllib3
+import re
+
+from streamlink.plugin import Plugin
+from streamlink.plugin.api import http, validate
+from streamlink.plugin.api import useragents
+from streamlink.stream import HLSStream
+
+_url_re = re.compile(r"https?://(www\.)?ok\.ru/live/\d+")
+_vod_re = re.compile(r";(?P<hlsurl>[^;]+video\.m3u8)")
+
+_schema = validate.Schema(
+    validate.transform(_vod_re.search),
+    validate.any(
+        None,
+        validate.all(
+            validate.get("hlsurl"),
+            validate.url()
+        )
+    )
+)
+
+class OK_live(Plugin):
+    """
+    Support for ok.ru live stream: http://www.ok.ru/live/
+    """
+    @classmethod
+    def can_handle_url(cls, url):
+        return _url_re.match(url) is not None
+
+    def _get_streams(self):
+        headers = {
+            'User-Agent': useragents.CHROME,
+            'Referer': self.url
+        }
+
+        hls  = http.get(self.url, headers=headers, schema=_schema)
+        return HLSStream.parse_variant_playlist(self.session, hls, headers=headers)
+
+
+__plugin__ = OK_live
\ No newline at end of file


Test patch:
diff --git a/tests/test_plugin_ok_live.py b/tests/test_plugin_ok_live.py
new file mode 100644
index 00000000..60a02bcc
--- /dev/null
+++ b/tests/test_plugin_ok_live.py
@@ -0,0 +1,15 @@
+import unittest
+
+from streamlink.plugins.ok_live import OK_live
+
+
+class TestPluginOK_live(unittest.TestCase):
+    def test_can_handle_url(self):
+        # should match
+        self.assertTrue(OK_live.can_handle_url("https://ok.ru/live/12345"))
+        self.assertTrue(OK_live.can_handle_url("http://ok.ru/live/12345"))
+        self.assertTrue(OK_live.can_handle_url("http://www.ok.ru/live/12345"))
+
+        # shouldn't match
+        self.assertFalse(OK_live.can_handle_url("http://www.tvcatchup.com/"))
+        self.assertFalse(OK_live.can_handle_url("http://www.youtube.com/"))