Repository: streamlink/streamlink
Pull request #1775 (2018-06-11 14:22:27)

plugins.VinhLongTV: New plugin for livestreams of thvli.vn
TV/Radio channel from Vietnam

https://www.youtube.com/user/VinhLongTV/about

---

Fixed https://github.com/streamlink/streamlink/issues/1762


Patch:
diff --git a/docs/plugin_matrix.rst b/docs/plugin_matrix.rst
index eeed9059..e8de3dee 100644
--- a/docs/plugin_matrix.rst
+++ b/docs/plugin_matrix.rst
@@ -254,6 +254,7 @@ viasat                  - juicyplay.dk       Yes   Yes   Streams may be geo-rest
                         - viafree.no
                         - viafree.se
 vidio                   vidio.com            Yes   Yes
+vinhlongtv              thvli.vn             Yes   No    Streams are geo-restricted to Vietnam
 vk                      vk.com               Yes   Yes
 vrtbe                   vrt.be/vrtnu         Yes   Yes
 webcast_india_gov       webcast.gov.in       Yes   No    You can use #Channel to indicate CH number.
diff --git a/src/streamlink/plugins/vinhlongtv.py b/src/streamlink/plugins/vinhlongtv.py
new file mode 100644
index 00000000..9e6b5b66
--- /dev/null
+++ b/src/streamlink/plugins/vinhlongtv.py
@@ -0,0 +1,45 @@
+import logging
+import re
+
+from streamlink.plugin import Plugin
+from streamlink.plugin.api import http, useragents, validate
+from streamlink.stream import HLSStream
+
+log = logging.getLogger(__name__)
+
+
+class VinhLongTV(Plugin):
+
+    api_url = 'http://api.thvli.vn/backend/cm/detail/{0}/'
+
+    _url_re = re.compile(
+        r'https?://(?:www\.)?thvli\.vn/live/(?P<channel>[^/]+)')
+
+    _data_schema = validate.Schema(
+        {
+            'link_play': validate.text,
+        },
+        validate.get('link_play')
+    )
+
+    @classmethod
+    def can_handle_url(cls, url):
+        return cls._url_re.match(url) is not None
+
+    def _get_streams(self):
+        http.headers.update({'User-Agent': useragents.FIREFOX})
+
+        channel = self._url_re.match(self.url).group('channel')
+
+        res = http.get(self.api_url.format(channel))
+        hls_url = http.json(res, schema=self._data_schema)
+        log.debug('URL={0}'.format(hls_url))
+
+        streams = HLSStream.parse_variant_playlist(self.session, hls_url)
+        if not streams:
+            return {'live': HLSStream(self.session, hls_url)}
+        else:
+            return streams
+
+
+__plugin__ = VinhLongTV


Test patch:
diff --git a/tests/test_plugin_vinhlongtv.py b/tests/test_plugin_vinhlongtv.py
new file mode 100644
index 00000000..a308166c
--- /dev/null
+++ b/tests/test_plugin_vinhlongtv.py
@@ -0,0 +1,20 @@
+import unittest
+
+from streamlink.plugins.vinhlongtv import VinhLongTV
+
+
+class TestPluginVinhLongTV(unittest.TestCase):
+    def test_can_handle_url(self):
+        should_match = [
+            'http://thvli.vn/live/thvl1-hd/aab94d1f-44e1-4992-8633-6d46da08db42',
+            'http://thvli.vn/live/thvl2-hd/bc60bddb-99ac-416e-be26-eb4d0852f5cc',
+            'http://thvli.vn/live/phat-thanh/c87174ba-7aeb-4cb4-af95-d59de715464c',
+        ]
+        for url in should_match:
+            self.assertTrue(VinhLongTV.can_handle_url(url))
+
+        should_not_match = [
+            'https://example.com/index.html',
+        ]
+        for url in should_not_match:
+            self.assertFalse(VinhLongTV.can_handle_url(url))