Repository: streamlink/streamlink

Issue:
Now News Live (News and Live channel)
## Plugin Request

<!-- Replace [ ] with [x] in order to check the box -->
- [x] This is a plugin request and I have read the contribution guidelines and plugin request requirements.


### Description

<!-- Explain the plugin and site as clearly as you can. What is the site about? Who runs it? What content does it provide? What value does it bring to Streamlink? Etc. -->
One of a local television station from Hong Kong which currently only accessible via Internet streaming or paid cable subscription. Now News is one of a few disinterested news channel which provide fare perspective reports compared to major aired television in Hong Kong.

### Example stream URLs

<!-- Example URLs for streams are required. Plugin requests which do not have example URLs will be closed. -->

1. https://news.now.com/home/live331a
2. https://news.now.com/home/live


### Additional comments, screenshots, etc.
![chrome_2019-08-18_20-27-07](https://user-images.githubusercontent.com/771955/63224427-92c4d700-c1f6-11e9-8010-500d19f97187.png)


[Love Streamlink? Please consider supporting our collective. Thanks!](https://opencollective.com/streamlink/donate)



Discussion:

Resolving patch:
diff --git a/src/streamlink/plugins/nownews.py b/src/streamlink/plugins/nownews.py
new file mode 100644
--- /dev/null
+++ b/src/streamlink/plugins/nownews.py
@@ -0,0 +1,49 @@
+import logging
+import re
+import json
+
+from streamlink.plugin import Plugin
+from streamlink.stream import HLSStream
+
+log = logging.getLogger(__name__)
+
+
+class NowNews(Plugin):
+    _url_re = re.compile(r"https?://news.now.com/home/live")
+    epg_re = re.compile(r'''epg.getEPG\("(\d+)"\);''')
+    api_url = "https://hkt-mobile-api.nowtv.now.com/09/1/getLiveURL"
+    backup_332_api = "https://d7lz7jwg8uwgn.cloudfront.net/apps_resource/news/live.json"
+    backup_332_stream = "https://d3i3yn6xwv1jpw.cloudfront.net/live/now332/playlist.m3u8"
+
+    @classmethod
+    def can_handle_url(cls, url):
+        return cls._url_re.match(url) is not None
+
+    def _get_streams(self):
+        res = self.session.http.get(self.url)
+        m = self.epg_re.search(res.text)
+        channel_id = m and m.group(1)
+        if channel_id:
+            log.debug("Channel ID: {0}".format(channel_id))
+
+            if channel_id == "332":
+                # there is a special backup stream for channel 332
+                bk_res = self.session.http.get(self.backup_332_api)
+                bk_data = self.session.http.json(bk_res)
+                if bk_data and bk_data["backup"]:
+                    log.info("Using backup stream for channel 332")
+                    return HLSStream.parse_variant_playlist(self.session, self.backup_332_stream)
+
+            api_res = self.session.http.post(self.api_url,
+                                             headers={"Content-Type": 'application/json'},
+                                             data=json.dumps(dict(channelno=channel_id,
+                                                                  mode="prod",
+                                                                  audioCode="",
+                                                                  format="HLS",
+                                                                  callerReferenceNo="20140702122500")))
+            data = self.session.http.json(api_res)
+            for stream_url in data.get("asset", {}).get("hls", {}).get("adaptive", []):
+                return HLSStream.parse_variant_playlist(self.session, stream_url)
+
+
+__plugin__ = NowNews


Test patch:
diff --git a/tests/plugins/test_nownews.py b/tests/plugins/test_nownews.py
new file mode 100644
--- /dev/null
+++ b/tests/plugins/test_nownews.py
@@ -0,0 +1,27 @@
+import unittest
+
+import pytest
+
+from streamlink.plugins.nownews import NowNews
+
+
+class TestPluginNowNews:
+    valid_urls = [
+        ("https://news.now.com/home/live",),
+        ("http://news.now.com/home/live",),
+        ("https://news.now.com/home/live331a",),
+        ("http://news.now.com/home/live331a",)
+    ]
+    invalid_urls = [
+        ("https://news.now.com/home/local",),
+        ("http://media.now.com.hk/",),
+        ("https://www.youtube.com",)
+    ]
+
+    @pytest.mark.parametrize(["url"], valid_urls)
+    def test_can_handle_url(self, url):
+        assert NowNews.can_handle_url(url), "url should be handled"
+
+    @pytest.mark.parametrize(["url"], invalid_urls)
+    def test_can_handle_url_negative(self, url):
+        assert not NowNews.can_handle_url(url), "url should not be handled"