Repository: streamlink/streamlink
Pull request #591 (2017-02-16 10:09:13)

plugins.tvrby: support for live streams of Belarus national TV
Add support for streaming live TV from the Belarus National TV channels.
Most channel provide one HLS stream and one RTMP stream, both are named "live" as they are equivalent and this respects the user's `stream-priority` setting when requesting "best".

Supported URLs:
- tvr.by/televidenie/belarus-1/
- tvr.by/televidenie/belarus-2/
- tvr.by/televidenie/belarus-3/
- tvr.by/televidenie/belarus-5/
- tvr.by/televidenie/belarus-24/

*Note: VLC3-beta does not currently play video for the `rtmp` streams, only audio.*


Patch:
diff --git a/docs/plugin_matrix.rst b/docs/plugin_matrix.rst
index ad34e8ab..fc21f116 100644
--- a/docs/plugin_matrix.rst
+++ b/docs/plugin_matrix.rst
@@ -167,6 +167,7 @@ tv8cat              tv8.cat              Yes   No    Streams may be geo-restrict
 tv360               tv360.com.tr         Yes   No
 tvcatchup           - tvcatchup.com      Yes   No    Streams may be geo-restricted to Great Britain.
 tvplayer            tvplayer.com         Yes   No    Streams may be geo-restricted to Great Britain. Premium streams are not supported.
+tvrby               tvr.by               Yes   No    Streams may be geo-restricted to Belarus.
 tvrplus             tvrplus.ro           Yes   No    Streams may be geo-restricted to Romania.
 twitch              twitch.tv            Yes   Yes   Possible to authenticate for access to
                                                      subscription streams.
diff --git a/src/streamlink/plugins/tvrby.py b/src/streamlink/plugins/tvrby.py
new file mode 100644
index 00000000..722e452b
--- /dev/null
+++ b/src/streamlink/plugins/tvrby.py
@@ -0,0 +1,58 @@
+from __future__ import print_function
+import re
+from wsgiref import headers
+
+from streamlink import PluginError
+from streamlink.plugin import Plugin
+from streamlink.plugin.api import http
+from streamlink.plugin.api import useragents
+from streamlink.plugin.api import validate
+from streamlink.stream import HLSStream
+from streamlink.compat import urlparse
+from streamlink.stream import RTMPStream
+
+
+class TVRBy(Plugin):
+    url_re = re.compile(r"""https?://(?:www\.)?tvr.by/televidenie/belarus""")
+    file_re = re.compile(r"""(?P<q1>["']?)file(?P=q1)\s*:\s*(?P<q2>["'])(?P<url>(?:http.+?m3u8.*?|rtmp://.*?))(?P=q2)""")
+
+    stream_schema = validate.Schema(
+        validate.all(
+            validate.transform(file_re.finditer),
+            validate.transform(list),
+            [validate.get("url")]
+        ),
+    )
+
+    def __init__(self, url):
+        # ensure the URL ends with a /
+        if not url.endswith("/"):
+            url += "/"
+        super(TVRBy, self).__init__(url)
+
+    @classmethod
+    def can_handle_url(cls, url):
+        return cls.url_re.match(url) is not None
+
+    def _get_streams(self):
+        res = http.get(self.url)
+        stream_urls = self.stream_schema.validate(res.text)
+        self.logger.debug("Found {0} stream URL{1}", len(stream_urls),
+                          "" if len(stream_urls) == 1 else "s")
+
+        for stream_url in stream_urls:
+            if "m3u8" in stream_url:
+                for _, s in HLSStream.parse_variant_playlist(self.session, stream_url).items():
+                    yield "live", s
+            if stream_url.startswith("rtmp://"):
+                a = stream_url.split("///")
+                s = RTMPStream(self.session, {
+                    "rtmp": a[0],
+                    "playpath": "live",
+                    "swfVfy": "http://www.tvr.by/plugines/uppod/uppod.swf",
+                    "pageUrl": self.url
+                })
+                yield "live", s
+
+
+__plugin__ = TVRBy


Test patch:
diff --git a/tests/test_plugin_tvrby.py b/tests/test_plugin_tvrby.py
new file mode 100644
index 00000000..c1348a78
--- /dev/null
+++ b/tests/test_plugin_tvrby.py
@@ -0,0 +1,31 @@
+import unittest
+
+from streamlink.plugins.tvrby import TVRBy
+
+
+class TestPluginTVRBy(unittest.TestCase):
+    def test_can_handle_url(self):
+        # should match
+        self.assertTrue(TVRBy.can_handle_url("http://www.tvr.by/televidenie/belarus-1/"))
+        self.assertTrue(TVRBy.can_handle_url("http://www.tvr.by/televidenie/belarus-1"))
+        self.assertTrue(TVRBy.can_handle_url("http://www.tvr.by/televidenie/belarus-24/"))
+        self.assertTrue(TVRBy.can_handle_url("http://www.tvr.by/televidenie/belarus-24"))
+
+        # shouldn't match
+        self.assertFalse(TVRBy.can_handle_url("http://www.tv8.cat/algo/"))
+        self.assertFalse(TVRBy.can_handle_url("http://www.tvcatchup.com/"))
+        self.assertFalse(TVRBy.can_handle_url("http://www.youtube.com/"))
+
+    def test_url_fix(self):
+        self.assertTrue(
+            "http://www.tvr.by/televidenie/belarus-1/",
+            TVRBy("http://www.tvr.by/televidenie/belarus-1/").url)
+        self.assertTrue(
+            "http://www.tvr.by/televidenie/belarus-1/",
+            TVRBy("http://www.tvr.by/televidenie/belarus-1").url)
+        self.assertTrue(
+            "http://www.tvr.by/televidenie/belarus-24/",
+            TVRBy("http://www.tvr.by/televidenie/belarus-24/").url)
+        self.assertTrue(
+            "http://www.tvr.by/televidenie/belarus-24/",
+            TVRBy("http://www.tvr.by/televidenie/belarus-24").url)