Repository: streamlink/streamlink
Pull request #965 (2017-05-30 11:12:14)

setup: exclude requests version 2.16 through 2.17.1
This is a change to the required version of `requests` to exclude some versions that give an unwanted warning about insecure requests for twitch.tv, fixing #963. 



Patch:
diff --git a/docs/cli.rst b/docs/cli.rst
index 9dd3d3e6..46c5f0ed 100644
--- a/docs/cli.rst
+++ b/docs/cli.rst
@@ -136,7 +136,7 @@ or for a option without value::
 
 .. note::
     Any quotes used will be part of the value, so only use when the value needs them,
-    e.g. specifying a player with a path containing spaces.
+    e.g. specifiying a player with a path containing spaces.
 
 Example
 ^^^^^^^
@@ -219,7 +219,7 @@ You can login like this:
 Once logged in, the plugin makes sure to save the session credentials to avoid
 asking your username and password again.
 
-Nevertheless, these credentials are valid for a limited amount of time, so it
+Neverthless, these credentials are valid for a limited amount of time, so it
 might be a good idea to save your username and password in your
 :ref:`configuration file <cli-streamlinkrc>` anyway.
 
diff --git a/docs/plugin_matrix.rst b/docs/plugin_matrix.rst
index 3498ac02..a524287f 100644
--- a/docs/plugin_matrix.rst
+++ b/docs/plugin_matrix.rst
@@ -146,13 +146,13 @@ openrectv           openrec.tv           Yes   Yes
 orf_tvthek          tvthek.orf.at        Yes   Yes
 ovvatv              ovva.tv              Yes   No
 pandatv             panda.tv             Yes   ?
+pcyourfreetv        pc-yourfreetv.com    Yes   --    Requires a login.
 periscope           periscope.tv         Yes   Yes   Replay/VOD is supported.
 picarto             picarto.tv           Yes   --
 playtv              playtv.fr            Yes   --    Streams may be geo-restricted to France.
 pluzz               - france.tv          Yes   Yes   Streams may be geo-restricted to France, Andorra and Monaco.
                     - ludo.fr
                     - zouzous.fr
-		    - france3-reg.. [8]_
 powerapp            powerapp.com.tr      Yes   No
 raiplay             raiplay.it           Yes   No    Most streams are geo-restricted to Italy.
 rtlxl               rtlxl.nl             No    Yes   Streams may be geo-restricted to The Netherlands. Livestreams not supported.
@@ -257,4 +257,3 @@ zhanqitv            zhanqi.tv            Yes   No
 .. [5] mediathek.daserste.de
 .. [6] players.brightcove.net
 .. [7] player.theplatform.com
-.. [8] france3-regions.francetvinfo.fr
diff --git a/setup.py b/setup.py
index a5f9bc86..6ddf2ca1 100644
--- a/setup.py
+++ b/setup.py
@@ -23,7 +23,7 @@ if version_info[0] == 2:
 if version_info[0] == 2 or (version_info[0] == 3 and version_info[1] < 4):
     deps.append("singledispatch")
 
-deps.append("requests>=2.2,!=2.12.0,!=2.12.1,<3.0")
+deps.append("requests>=2.2,!=2.12.0,!=2.12.1,!=2.16.0,!=2.16.1,!=2.16.2,!=2.16.3,!=2.16.4,!=2.16.5,!=2.17.1,<3.0")
 
 # for encrypted streams
 if environ.get("STREAMLINK_USE_PYCRYPTO"):
diff --git a/src/streamlink/plugins/app17.py b/src/streamlink/plugins/app17.py
index b4cecb73..d0a17598 100644
--- a/src/streamlink/plugins/app17.py
+++ b/src/streamlink/plugins/app17.py
@@ -31,7 +31,6 @@ class App17(Plugin):
     def _get_streams(self):
         match = _url_re.match(self.url)
         page = match.group("page")
-        channel = match.group("channel")
 
         http.headers.update({'User-Agent': useragents.CHROME})
 
@@ -45,16 +44,6 @@ class App17(Plugin):
                 self.logger.info("Stream currently unavailable.")
                 return
 
-            url = ROOM_URL.format(rid)
-            res = http.get(url)
-        elif page == 'profile':
-            api = http.post(API_URL, data={"targetUserID": channel})
-            data = http.json(api, schema=_user_api_schema)
-            rid = data["liveStreamID"]
-            if rid == 0:
-                self.logger.info("Stream currently unavailable.")
-                return
-
             url = ROOM_URL.format(rid)
             res = http.get(url)
         else:
@@ -78,16 +67,13 @@ class App17(Plugin):
         else:
             yield "live", HTTPStream(self.session, url)
 
-        if '17app.co' in url:
-            prefix = url.replace("rtmp:", "http:").replace(".flv", "/playlist.m3u8")
-            if '/playlist.m3u8' not in prefix:
-                url = prefix + "/playlist.m3u8"
-            else:
-                url = prefix
+        prefix = url.replace("rtmp:", "http:").replace(".flv", ".m3u8")
+        if '.m3u8' not in prefix:
+            url = prefix + "/playlist.m3u8"
             for stream in HLSStream.parse_variant_playlist(self.session, url).items():
                 yield stream
         else:
-            url = url.replace(".flv", ".m3u8")
+            url = prefix
             yield "live", HLSStream(self.session, url)
 
 
diff --git a/src/streamlink/plugins/pcyourfreetv.py b/src/streamlink/plugins/pcyourfreetv.py
new file mode 100644
index 00000000..cf253c2c
--- /dev/null
+++ b/src/streamlink/plugins/pcyourfreetv.py
@@ -0,0 +1,79 @@
+import re
+
+from streamlink.compat import unquote
+from streamlink.plugin import Plugin, PluginOptions
+from streamlink.plugin.api import http
+from streamlink.stream import HLSStream
+
+
+class PCYourFreeTV(Plugin):
+    _login_url = 'http://pc-yourfreetv.com/home.php'
+    _url_re = re.compile(r'http://pc-yourfreetv\.com/index_player\.php\?channel=.+?&page_id=\d+')
+    _iframe_re = re.compile(r"<iframe .*?\bsrc='(?P<iframe>.+?)'.*?</iframe>")
+    _player_re = re.compile(r"<script language=JavaScript>m='(?P<player>.+?)'", re.DOTALL)
+    _video_url_re = re.compile(r'new [a-zA-Z0-9]+.Player\({source: "(?P<video_url>[^"]+?)".+?}.*\);', re.DOTALL)
+
+    options = PluginOptions({
+        'username': None,
+        'password': None
+    })
+
+    @classmethod
+    def can_handle_url(cls, url):
+        return PCYourFreeTV._url_re.match(url)
+
+    def login(self, username, password):
+        res = http.post(
+            self._login_url,
+            data={
+                'user_name': username,
+                'user_pass': password,
+                'login': 'Login'
+            }
+        )
+
+        return username in res.text
+
+    def _get_streams(self):
+        username = self.get_option('username')
+        password = self.get_option('password')
+
+        if username is None or password is None:
+            self.logger.error("PC-YourFreeTV requires authentication, use --pcyourfreetv-username "
+                              "and --pcyourfreetv-password to set your username/password combination")
+            return
+
+        if self.login(username, password):
+            self.logger.info("Successfully logged in as {0}", username)
+
+        # Retrieve URL iframe
+        res = http.get(self.url)
+        match = self._iframe_re.search(res.text)
+        if match is None:
+            return
+
+        res = http.get(match.group('iframe'))
+        players = self._player_re.findall(res.text)
+        if len(players) == 0 is None:
+            return
+
+        while len(players) > 0:
+            player = unquote(players[-1])
+            players = self._player_re.findall(player)
+
+        match = self._video_url_re.search(player)
+        if match is None:
+            return
+
+        video_url = match.group('video_url')
+        if '.m3u8' in video_url:
+            streams = HLSStream.parse_variant_playlist(self.session, video_url)
+            if len(streams) != 0:
+                for stream in streams.items():
+                    yield stream
+            else:
+                # Not a HLS playlist
+                yield 'live', HLSStream(self.session, video_url)
+
+
+__plugin__ = PCYourFreeTV
diff --git a/src/streamlink/plugins/pluzz.py b/src/streamlink/plugins/pluzz.py
index 59555a26..0e9377c0 100644
--- a/src/streamlink/plugins/pluzz.py
+++ b/src/streamlink/plugins/pluzz.py
@@ -15,10 +15,9 @@ class Pluzz(Plugin):
     PLAYER_GENERATOR_URL = 'https://sivideo.webservices.francetelevisions.fr/assets/staticmd5/getUrl?id=jquery.player.7.js'
     TOKEN_URL = 'http://hdfauthftv-a.akamaihd.net/esi/TA?url={0}'
 
-    _url_re = re.compile(r'https?://((?:www)\.france\.tv/.+\.html|www\.(ludo|zouzous)\.fr/heros/[\w-]+|france3-regions\.francetvinfo\.fr/.+?/tv/direct)')
+    _url_re = re.compile(r'https?://((?:www)\.france\.tv/.+\.html|www\.(ludo|zouzous)\.fr/heros/[\w-]+)')
     _pluzz_video_id_re = re.compile(r'data-main-video="(?P<video_id>.+?)"')
-    _jeunesse_video_id_re = re.compile(r'playlist: \[{.*?,"identity":"(?P<video_id>.+?)@(?P<catalogue>Ludo|Zouzous)"')
-    _f3_regions_video_id_re = re.compile(r'"http://videos\.francetv\.fr/video/(?P<video_id>.+)@Regions"')
+    _other_video_id_re = re.compile(r'playlist: \[{.*?,"identity":"(?P<video_id>.+?)@(?P<catalogue>Ludo|Zouzous)"')
     _player_re = re.compile(r'src="(?P<player>//staticftv-a\.akamaihd\.net/player/jquery\.player.+?-[0-9a-f]+?\.js)"></script>')
     _swf_re = re.compile(r'//staticftv-a\.akamaihd\.net/player/bower_components/player_flash/dist/FranceTVNVPVFlashPlayer\.akamai-[0-9a-f]+\.swf')
     _hds_pv_data_re = re.compile(r"~data=.+?!")
@@ -90,12 +89,7 @@ class Pluzz(Plugin):
 
         # Retrieve URL page and search for video ID
         res = http.get(self.url)
-        if 'france.tv' in self.url:
-            match = self._pluzz_video_id_re.search(res.text)
-        elif 'ludo.fr' in self.url or 'zouzous.fr' in self.url:
-            match = self._jeunesse_video_id_re.search(res.text)
-        elif 'france3-regions.francetvinfo.fr' in self.url:
-            match = self._f3_regions_video_id_re.search(res.text)
+        match = self._pluzz_video_id_re.search(res.text) or self._other_video_id_re.search(res.text)
         if match is None:
             return
         video_id = match.group('video_id')
diff --git a/src/streamlink_cli/argparser.py b/src/streamlink_cli/argparser.py
index df890818..10c99a57 100644
--- a/src/streamlink_cli/argparser.py
+++ b/src/streamlink_cli/argparser.py
@@ -1230,6 +1230,20 @@ plugin.add_argument(
     A LiveEdu account password to use with --liveedu-email.
     """
 )
+plugin.add_argument(
+    "--pcyourfreetv-username",
+    metavar="USERNAME",
+    help="""
+    The username used to register with pc-yourfreetv.com.
+    """
+)
+plugin.add_argument(
+    "--pcyourfreetv-password",
+    metavar="PASSWORD",
+    help="""
+    A pc-yourfreetv.com account password to use with --pcyourfreetv-username.
+    """
+)
 plugin.add_argument(
     "--bbciplayer-username",
     metavar="USERNAME",
diff --git a/src/streamlink_cli/main.py b/src/streamlink_cli/main.py
index fd70b14d..e111b530 100644
--- a/src/streamlink_cli/main.py
+++ b/src/streamlink_cli/main.py
@@ -919,6 +919,17 @@ def setup_plugin_options():
     if liveedu_password:
         streamlink.set_plugin_option("liveedu", "password", liveedu_password)
 
+    if args.pcyourfreetv_username:
+        streamlink.set_plugin_option("pcyourfreetv", "username", args.pcyourfreetv_username)
+
+    if args.pcyourfreetv_username and not args.pcyourfreetv_password:
+        pcyourfreetv_password = console.askpass("Enter pc-yourfreetv.com password: ")
+    else:
+        pcyourfreetv_password = args.pcyourfreetv_password
+
+    if pcyourfreetv_password:
+        streamlink.set_plugin_option("pcyourfreetv", "password", pcyourfreetv_password)
+
     if args.bbciplayer_username:
         streamlink.set_plugin_option("bbciplayer", "username", args.bbciplayer_username)
 


Test patch:
diff --git a/tests/test_plugin_pcyourfreetv.py b/tests/test_plugin_pcyourfreetv.py
new file mode 100644
index 00000000..d1ee96dc
--- /dev/null
+++ b/tests/test_plugin_pcyourfreetv.py
@@ -0,0 +1,18 @@
+import unittest
+
+from streamlink.plugins.pcyourfreetv import PCYourFreeTV
+
+
+class TestPluginPCYourFreeTV(unittest.TestCase):
+    def test_can_handle_url(self):
+        # should match
+        self.assertTrue(PCYourFreeTV.can_handle_url("http://pc-yourfreetv.com/index_player.php?channel=das%20erste&page_id=41"))
+        self.assertTrue(PCYourFreeTV.can_handle_url("http://pc-yourfreetv.com/index_player.php?channel=srf%20eins&page_id=41"))
+        self.assertTrue(PCYourFreeTV.can_handle_url("http://pc-yourfreetv.com/index_player.php?channel=bbc%20one&page_id=41"))
+        self.assertTrue(PCYourFreeTV.can_handle_url("http://pc-yourfreetv.com/index_player.php?channel=tf1&page_id=41"))
+
+        # shouldn't match
+        self.assertFalse(PCYourFreeTV.can_handle_url("http://pc-yourfreetv.com/home.php"))
+        self.assertFalse(PCYourFreeTV.can_handle_url("http://pc-yourfreetv.com/index_livetv.php?page_id=1"))
+        self.assertFalse(PCYourFreeTV.can_handle_url("http://tvcatchup.com/"))
+        self.assertFalse(PCYourFreeTV.can_handle_url("http://youtube.com/"))
diff --git a/tests/test_plugin_pluzz.py b/tests/test_plugin_pluzz.py
index 9528479d..cac35e71 100644
--- a/tests/test_plugin_pluzz.py
+++ b/tests/test_plugin_pluzz.py
@@ -19,7 +19,6 @@ class TestPluginPluzz(unittest.TestCase):
         self.assertTrue(Pluzz.can_handle_url("http://www.ludo.fr/heros/il-etait-une-fois-la-vie"))
         self.assertTrue(Pluzz.can_handle_url("http://www.zouzous.fr/heros/oui-oui"))
         self.assertTrue(Pluzz.can_handle_url("http://www.zouzous.fr/heros/marsupilami-1"))
-        self.assertTrue(Pluzz.can_handle_url("http://france3-regions.francetvinfo.fr/bourgogne-franche-comte/tv/direct/franche-comte"))
 
         # shouldn't match
         self.assertFalse(Pluzz.can_handle_url("http://www.france.tv/"))
@@ -27,6 +26,5 @@ class TestPluginPluzz(unittest.TestCase):
         self.assertFalse(Pluzz.can_handle_url("http://www.ludo.fr/"))
         self.assertFalse(Pluzz.can_handle_url("http://www.ludo.fr/jeux"))
         self.assertFalse(Pluzz.can_handle_url("http://www.zouzous.fr/"))
-        self.assertFalse(Pluzz.can_handle_url("http://france3-regions.francetvinfo.fr/"))
         self.assertFalse(Pluzz.can_handle_url("http://www.tvcatchup.com/"))
         self.assertFalse(Pluzz.can_handle_url("http://www.youtube.com/"))