Repository: streamlink/streamlink
Pull request #1281 (2017-10-22 15:10:02)

plugins.canlitv: fix URLs

gravyboat: The tests associated with the plugin failed, can you please take a look and fix the errors?

```
=================================== FAILURES ===================================

____________________ TestPluginCanlitv.test_can_handle_url _____________________

self = <tests.test_plugin_canlitv.TestPluginCanlitv testMethod=test_can_handle_url>

def test_can_handle_url(self):

# should match

self.assertTrue(Canlitv.can_handle_url("http://www.canlitv.com/channel"))

>       self.assertTrue(Canlitv.can_handle_url("http://www.canlitv.life/channel"))

E AssertionError: None is not true

tests/test_plugin_canlitv.py:24: AssertionError

=============== 1 failed, 395 passed, 5 skipped in 13.03 seconds ===============

The command "python -m pytest tests/" exited with 1.
```
codecov[bot]: # [Codecov](https://codecov.io/gh/streamlink/streamlink/pull/1281?src=pr&el=h1) Report
> Merging [#1281](https://codecov.io/gh/streamlink/streamlink/pull/1281?src=pr&el=desc) into [master](https://codecov.io/gh/streamlink/streamlink/commit/1546245e2181993230bb3358c0da4d0a92ee8003?src=pr&el=desc) will **decrease** coverage by `0.01%`.
> The diff coverage is `25%`.


```diff
@@            Coverage Diff             @@
##           master    #1281      +/-   ##
==========================================
- Coverage   47.53%   47.52%   -0.02%     
==========================================
  Files          98       98              
  Lines        7140     7142       +2     
==========================================
  Hits         3394     3394              
- Misses       3746     3748       +2
```

gravyboat: These test changes don't do a check against `canlitv.plus` and simply remove life. You should just be able to add another assert true for the new URL.
emraher: Tried to make `test_plugin_canlitv.py` look like the original one. I hope this time it's OK.

Patch:
diff --git a/src/streamlink/plugins/canlitv.py b/src/streamlink/plugins/canlitv.py
index 64c73fa3..7c56102a 100644
--- a/src/streamlink/plugins/canlitv.py
+++ b/src/streamlink/plugins/canlitv.py
@@ -5,14 +5,14 @@ from streamlink.plugin.api import http
 from streamlink.plugin.api import useragents
 from streamlink.stream import HLSStream
 
-EMBED_URL_1 = "http://www.canlitv.life/kanallar.php?kanal={0}"
+EMBED_URL_1 = "http://www.canlitv.plus/kanallar.php?kanal={0}"
 EMBED_URL_2 = "http://www.ecanlitvizle.net/embed.php?kanal={0}"
 
 _m3u8_re = re.compile(r"""file\s*:\s*['"](?P<url>[^"']+)['"]""")
 _url_re = re.compile(r"""http(s)?://(?:www\.)?(?P<domain>
-    canlitv\.(com|life)
+    canlitv\.(com|plus)
     |
-    canlitvlive\.(co|live)
+    canlitvlive\.(io|co|live|site)
     |
     ecanlitvizle\.net
     )
@@ -35,7 +35,7 @@ class Canlitv(Plugin):
             "User-Agent": useragents.FIREFOX
         }
 
-        if domain == "canlitv.life":
+        if domain == "canlitv.plus":
             res = http.get(EMBED_URL_1.format(channel), headers=headers)
         elif domain == "ecanlitvizle.net":
             res = http.get(EMBED_URL_2.format(channel), headers=headers)
@@ -47,6 +47,9 @@ class Canlitv(Plugin):
         if url_match:
             hls_url = url_match.group("url")
 
+            if domain in ("canlitvlive.live", "canlitvlive.site"):
+                 hls_url = "http:" + hls_url
+
             self.logger.debug("Found URL: {0}".format(hls_url))
 
             try:
diff --git a/src/streamlink/plugins/pandatv.py b/src/streamlink/plugins/pandatv.py
index 324b1e76..ab8de87d 100644
--- a/src/streamlink/plugins/pandatv.py
+++ b/src/streamlink/plugins/pandatv.py
@@ -1,24 +1,27 @@
-"""Plugin for panda.tv by Fat Deer"""
-
 import re
 import types
 import time
 import json
 
+from streamlink.compat import quote
 from streamlink.plugin import Plugin
 from streamlink.plugin.api import http, validate
 from streamlink.stream import HTTPStream
 
-ROOM_API = "http://www.panda.tv/api_room_v3?roomid={0}&roomkey={1}&_={2}"
-ROOM_API_V2 = "http://www.panda.tv/api_room_v2?roomid={0}&_={1}"
-SD_URL_PATTERN = "http://pl{0}.live.panda.tv/live_panda/{1}.flv?sign={2}&ts={3}&rid={4}"
-HD_URL_PATTERN = "http://pl{0}.live.panda.tv/live_panda/{1}_mid.flv?sign={2}&ts={3}&rid={4}"
-OD_URL_PATTERN = "http://pl{0}.live.panda.tv/live_panda/{1}_small.flv?sign={2}&ts={3}&rid={4}"
+ROOM_API = "https://www.panda.tv/api_room_v3?token=&hostid={0}&roomid={1}&roomkey={2}&_={3}&param={4}&time={5}&sign={6}"
+ROOM_API_V2 = "https://www.panda.tv/api_room_v2?roomid={0}&_={1}"
+SD_URL_PATTERN = "https://pl{0}.live.panda.tv/live_panda/{1}.flv?sign={2}&ts={3}&rid={4}"
+HD_URL_PATTERN = "https://pl{0}.live.panda.tv/live_panda/{1}_mid.flv?sign={2}&ts={3}&rid={4}"
+OD_URL_PATTERN = "https://pl{0}.live.panda.tv/live_panda/{1}_small.flv?sign={2}&ts={3}&rid={4}"
 
 _url_re = re.compile(r"http(s)?://(\w+.)?panda.tv/(?P<channel>[^/&?]+)")
 _room_id_re = re.compile(r'data-room-id="(\d+)"')
 _status_re = re.compile(r'"status"\s*:\s*"(\d+)"\s*,\s*"display_type"')
 _room_key_re = re.compile(r'"room_key"\s*:\s*"(.+?)"')
+_hostid_re = re.compile(r'\\"hostid\\"\s*:\s*\\"(.+?)\\"')
+_param_re = re.compile(r'"param"\s*:\s*"(.+?)"\s*,\s*"time"')
+_time_re = re.compile(r'"time"\s*:\s*(\d+)')
+_sign_re = re.compile(r'"sign"\s*:\s*"(.+?)"')
 _sd_re = re.compile(r'"SD"\s*:\s*"(\d+)"')
 _hd_re = re.compile(r'"HD"\s*:\s*"(\d+)"')
 _od_re = re.compile(r'"OD"\s*:\s*"(\d+)"')
@@ -44,7 +47,7 @@ _room_schema = validate.Schema(
 
 class Pandatv(Plugin):
     @classmethod
-    def can_handle_url(self, url):
+    def can_handle_url(cls, url):
         return _url_re.match(url)
 
     def _get_streams(self):
@@ -64,6 +67,10 @@ class Pandatv(Plugin):
         try:
             status = _status_re.search(res.text).group(1)
             room_key = _room_key_re.search(res.text).group(1)
+            hostid = _hostid_re.search(res.text).group(1)
+            param = _param_re.search(res.text).group(1)
+            tt = _time_re.search(res.text).group(1)
+            sign = _sign_re.search(res.text).group(1)
             sd = _sd_re.search(res.text).group(1)
             hd = _hd_re.search(res.text).group(1)
             od = _od_re.search(res.text).group(1)
@@ -76,7 +83,9 @@ class Pandatv(Plugin):
             return
 
         ts = int(time.time())
-        url = ROOM_API.format(channel, room_key, ts)
+        param = param.replace("\\", "")
+        param = quote(param)
+        url = ROOM_API.format(hostid, channel, room_key, ts, param, tt, sign)
         room = http.get(url)
         data = http.json(room, schema=_room_schema)
         if not isinstance(data, dict):
@@ -95,17 +104,24 @@ class Pandatv(Plugin):
             self.logger.info("Please Check PandaTV Room API")
             return
 
-        plflag0 = plflag.split('_')[1]
-        if plflag0 != '3':
-            plflag1 = '4'
-        else:
-            plflag1 = '3'
-
         plflag_list = json.loads(plflag_list)
+        backup = plflag_list["backup"]
         rid = plflag_list["auth"]["rid"]
         sign = plflag_list["auth"]["sign"]
         ts = plflag_list["auth"]["time"]
 
+        backup.append(plflag)
+        plflag0 = backup
+        plflag0 = [i.split('_')[1] for i in plflag0] 
+
+        # let wangsu cdn priority, flag can see here in "H5PLAYER_CDN_LINES":
+        # https://www.panda.tv/cmstatic/global-config.js
+        lines = ["3", "4"]
+        try:
+            plflag1 = [i for i in plflag0 if i in lines][0]
+        except IndexError:
+            plflag1 = plflag0[0]
+
         if sd == '1':
             streams['ehq'] = HTTPStream(self.session, SD_URL_PATTERN.format(plflag1, room_key, sign, ts, rid))
 
diff --git a/src/streamlink/plugins/zattoo.py b/src/streamlink/plugins/zattoo.py
index 966500eb..37e6e2bd 100644
--- a/src/streamlink/plugins/zattoo.py
+++ b/src/streamlink/plugins/zattoo.py
@@ -57,7 +57,7 @@ class Zattoo(Plugin):
         self._session_attributes = Cache(filename='plugin-cache.json', key_prefix='zattoo:attributes')
         self._authed = self._session_attributes.get('beaker.session.id') and self._session_attributes.get('pzuid') and self._session_attributes.get('power_guide_hash')
         self._uuid = self._session_attributes.get('uuid')
-        self._expires = self._session_attributes.get('expires')
+        self._expires = self._session_attributes.get('expires', 946684800)
 
         self.base_url = 'https://{0}'.format(Zattoo._url_re.match(url).group('base_url'))
         self.headers = {


Test patch:
diff --git a/tests/test_plugin_canlitv.py b/tests/test_plugin_canlitv.py
index 0c5c69c4..58041011 100644
--- a/tests/test_plugin_canlitv.py
+++ b/tests/test_plugin_canlitv.py
@@ -20,13 +20,22 @@ class TestPluginCanlitv(unittest.TestCase):
 
     def test_can_handle_url(self):
         # should match
+        self.assertTrue(Canlitv.can_handle_url("http://www.canlitv.plus/channel"))
         self.assertTrue(Canlitv.can_handle_url("http://www.canlitv.com/channel"))
-        self.assertTrue(Canlitv.can_handle_url("http://www.canlitv.life/channel"))
         self.assertTrue(Canlitv.can_handle_url("http://www.canlitvlive.co/izle/channel.html"))
         self.assertTrue(Canlitv.can_handle_url("http://www.canlitvlive.live/izle/channel.html"))
+        self.assertTrue(Canlitv.can_handle_url("http://www.canlitvlive.io/izle/channel.html"))
+        self.assertTrue(Canlitv.can_handle_url("http://www.canlitvlive.site/izle/channel.html"))
         self.assertTrue(Canlitv.can_handle_url("http://www.ecanlitvizle.net/channel/"))
         self.assertTrue(Canlitv.can_handle_url("http://www.ecanlitvizle.net/onizleme.php?kanal=channel"))
         self.assertTrue(Canlitv.can_handle_url("http://www.ecanlitvizle.net/tv.php?kanal=channel"))
         # shouldn't match
         self.assertFalse(Canlitv.can_handle_url("http://www.canlitv.com"))
+        self.assertFalse(Canlitv.can_handle_url("http://www.canlitv.plus"))
         self.assertFalse(Canlitv.can_handle_url("http://www.ecanlitvizle.net"))
+        self.assertFalse(Canlitv.can_handle_url("http://www.canlitvlive.co"))
+        self.assertFalse(Canlitv.can_handle_url("http://www.canlitvlive.live"))
+        self.assertFalse(Canlitv.can_handle_url("http://www.canlitvlive.io"))
+        self.assertFalse(Canlitv.can_handle_url("http://www.canlitvlive.site"))
+        self.assertFalse(Canlitv.can_handle_url("http://www.ecanlitvizle.net"))
+