Repository: streamlink/streamlink
Pull request #2188 (2018-12-01 22:54:17)

plugins.vtvgo: New plugin for https://vtvgo.vn/
VTV is the national television broadcaster of Vietnam.
https://en.wikipedia.org/wiki/Vietnam_Television

closes https://github.com/streamlink/streamlink/issues/1536


Patch:
diff --git a/docs/plugin_matrix.rst b/docs/plugin_matrix.rst
index 81d3ec1d..6b84fe48 100644
--- a/docs/plugin_matrix.rst
+++ b/docs/plugin_matrix.rst
@@ -250,6 +250,7 @@ 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
+vtvgo                   vtvgo.vn             Yes   No
 webcast_india_gov       webcast.gov.in       Yes   No    You can use #Channel to indicate CH number.
 webtv                   web.tv               Yes   --
 welt                    welt.de              Yes   Yes   Streams may be geo-restricted to Germany.
diff --git a/src/streamlink/plugins/vtvgo.py b/src/streamlink/plugins/vtvgo.py
new file mode 100644
index 00000000..a2be57cd
--- /dev/null
+++ b/src/streamlink/plugins/vtvgo.py
@@ -0,0 +1,45 @@
+import logging
+import re
+
+from streamlink.exceptions import PluginError
+from streamlink.plugin import Plugin
+from streamlink.plugin.api import useragents
+from streamlink.stream import HLSStream
+
+log = logging.getLogger(__name__)
+
+
+class VTVgo(Plugin):
+
+    AJAX_URL = 'https://vtvgo.vn/ajax-get-stream'
+
+    _url_re = re.compile(r'https://vtvgo\.vn/xem-truc-tuyen-kenh-')
+    _params_re = re.compile(r'''var\s(?P<key>(?:type_)?id|time|token)\s*=\s*["']?(?P<value>[^"']+)["']?;''')
+
+    @classmethod
+    def can_handle_url(cls, url):
+        return cls._url_re.match(url) is not None
+
+    def _get_streams(self):
+        self.session.http.headers.update({
+            'Origin': 'https://vtvgo.vn',
+            'Referer': self.url,
+            'User-Agent': useragents.FIREFOX,
+            'X-Requested-With': 'XMLHttpRequest',
+        })
+        res = self.session.http.get(self.url)
+
+        params = self._params_re.findall(res.text)
+        if not params:
+            raise PluginError('No POST data')
+        elif len(params) != 4:
+            raise PluginError('Not enough POST data: {0!r}'.format(params))
+
+        log.trace('{0!r}'.format(params))
+        res = self.session.http.post(self.AJAX_URL, data=dict(params))
+        hls_url = self.session.http.json(res)['stream_url'][0]
+
+        return HLSStream.parse_variant_playlist(self.session, hls_url)
+
+
+__plugin__ = VTVgo


Test patch:
diff --git a/tests/plugins/test_vtvgo.py b/tests/plugins/test_vtvgo.py
new file mode 100644
index 00000000..7cfacf5c
--- /dev/null
+++ b/tests/plugins/test_vtvgo.py
@@ -0,0 +1,32 @@
+import unittest
+
+from streamlink.plugins.vtvgo import VTVgo
+
+
+class TestPluginVTVgo(unittest.TestCase):
+    def test_can_handle_url(self):
+        should_match = [
+            'https://vtvgo.vn/xem-truc-tuyen-kenh-vtv1-1.html',
+            'https://vtvgo.vn/xem-truc-tuyen-kenh-vtv2-2.html',
+            'https://vtvgo.vn/xem-truc-tuyen-kenh-vtv3-3.html',
+            'https://vtvgo.vn/xem-truc-tuyen-kenh-vtv4-4.html',
+            'https://vtvgo.vn/xem-truc-tuyen-kenh-vtv5-5.html',
+            'https://vtvgo.vn/xem-truc-tuyen-kenh-vtv6-6.html',
+            'https://vtvgo.vn/xem-truc-tuyen-kenh-vtv7-27.html',
+            'https://vtvgo.vn/xem-truc-tuyen-kenh-vtv8-36.html',
+            'https://vtvgo.vn/xem-truc-tuyen-kenh-vtv9-39.html',
+            'https://vtvgo.vn/xem-truc-tuyen-kenh-vtv5-t%C3%A2y-nam-b%E1%BB%99-7.html',
+            'https://vtvgo.vn/xem-truc-tuyen-kenh-k%C3%AAnh-vtv5-t%C3%A2y-nguy%C3%AAn-163.html',
+        ]
+        for url in should_match:
+            self.assertTrue(VTVgo.can_handle_url(url))
+
+        should_not_match = [
+            'https://example.com/index.html',
+            # POST request will error with www.
+            'https://www.vtvgo.vn/xem-truc-tuyen-kenh-vtv1-1.html',
+            # POST request will error with http://
+            'http://vtvgo.vn/xem-truc-tuyen-kenh-vtv1-1.html',
+        ]
+        for url in should_not_match:
+            self.assertFalse(VTVgo.can_handle_url(url))