Repository: streamlink/streamlink
Pull request #385 (2017-01-09 02:06:39)

Fix ArteTV plugin
* Fixes https://github.com/chrippa/livestreamer/issues/1281 due to site changes
* Add support for HDS streams
* Add tests

codecov-io: ## [Current coverage](https://codecov.io/gh/streamlink/streamlink/pull/385?src=pr) is 49.11% (diff: 23.52%)
> Merging [#385](https://codecov.io/gh/streamlink/streamlink/pull/385?src=pr) into [master](https://codecov.io/gh/streamlink/streamlink/branch/master?src=pr) will decrease coverage by **0.25%**

```diff
@@             master       #385   diff @@
==========================================
  Files            46         47     +1   
  Lines          4231       4288    +57   
  Methods           0          0          
  Messages          0          0          
  Branches          0          0          
==========================================
+ Hits           2089       2106    +17   
- Misses         2142       2182    +40   
  Partials          0          0          
```

> Powered by [Codecov](https://codecov.io?src=pr). Last update [98d11c8...f098b03](https://codecov.io/gh/streamlink/streamlink/compare/98d11c8e1cb309433a9270c386de5af1f72c380e...f098b03458d31c559abd19a16a5876ef87ec1dc2?src=pr)
gravyboat: Looks good to me! Thanks!

Patch:
diff --git a/src/streamlink/plugins/artetv.py b/src/streamlink/plugins/artetv.py
index 3565a128..8cf78f37 100644
--- a/src/streamlink/plugins/artetv.py
+++ b/src/streamlink/plugins/artetv.py
@@ -7,23 +7,21 @@ from itertools import chain
 from streamlink.compat import urlparse
 from streamlink.plugin import Plugin
 from streamlink.plugin.api import http, validate
-from streamlink.stream import HLSStream, HTTPStream, RTMPStream
+from streamlink.stream import HDSStream, HLSStream, HTTPStream, RTMPStream
 
 SWF_URL = "http://www.arte.tv/player/v2/jwplayer6/mediaplayer.6.6.swf"
-
-_url_re = re.compile(r"http(s)?://(\w+\.)?arte.tv/")
-_json_re = re.compile(r'arte_vp_(?:live-)?url=([\'"])(.+?)\1')
-
-_schema = validate.Schema(
-    validate.transform(_json_re.search),
-    validate.any(
-        None,
-        validate.all(
-            validate.get(2),
-            validate.url(scheme="http")
-        )
+JSON_VOD_URL = "https://api.arte.tv/api/player/v1/config/{}/{}"
+JSON_LIVE_URL = "https://api.arte.tv/api/player/v1/livestream/{}"
+
+_url_re = re.compile(r"""
+    https?://(?:\w+\.)?arte.tv/guide/
+    (?P<language>[a-z]{2})/
+    (?:
+        (?P<video_id>.+?)/.+ | # VOD
+        (?:direct|live)        # Live TV
     )
-)
+""", re.VERBOSE)
+
 _video_schema = validate.Schema({
     "videoJsonPlayer": {
         "VSR": validate.any(
@@ -41,7 +39,6 @@ _video_schema = validate.Schema({
     }
 })
 
-
 class ArteTV(Plugin):
     @classmethod
     def can_handle_url(self, url):
@@ -65,6 +62,15 @@ class ArteTV(Plugin):
             else:
                 yield stream_name, HTTPStream(self.session, stream_url)
 
+        elif stream_type == "f4m":
+            try:
+                streams = HDSStream.parse_manifest(self.session, stream_url)
+
+                for stream in streams.items():
+                    yield stream
+            except IOError as err:
+                self.logger.error("Failed to extract HDS streams: {0}", err)
+
         elif stream_type == "rtmp":
             params = {
                 "rtmp": stream["streamer"],
@@ -82,10 +88,13 @@ class ArteTV(Plugin):
             yield stream_name, stream
 
     def _get_streams(self):
-        json_url = http.get(self.url, schema=_schema)
-        if not json_url:
-            return
-
+        match = _url_re.match(self.url)
+        language = match.group('language')
+        video_id = match.group('video_id')
+        if video_id is None:
+            json_url = JSON_LIVE_URL.format(language)
+        else:
+            json_url = JSON_VOD_URL.format(language, video_id)
         res = http.get(json_url)
         video = http.json(res, schema=_video_schema)
 


Test patch:
diff --git a/tests/test_plugin_artetv.py b/tests/test_plugin_artetv.py
new file mode 100644
index 00000000..033d1edf
--- /dev/null
+++ b/tests/test_plugin_artetv.py
@@ -0,0 +1,21 @@
+import unittest
+
+from streamlink.plugins.artetv import ArteTV
+
+
+class TestPluginArteTV(unittest.TestCase):
+    def test_can_handle_url(self):
+        # should match
+        self.assertTrue(ArteTV.can_handle_url("http://www.arte.tv/guide/fr/direct"))
+        self.assertTrue(ArteTV.can_handle_url("http://www.arte.tv/guide/de/live"))
+        self.assertTrue(ArteTV.can_handle_url("http://www.arte.tv/guide/fr/024031-000-A/le-testament-du-docteur-mabuse"))
+        self.assertTrue(ArteTV.can_handle_url("http://www.arte.tv/guide/de/024031-000-A/das-testament-des-dr-mabuse"))
+        self.assertTrue(ArteTV.can_handle_url("http://www.arte.tv/guide/en/072544-002-A/christmas-carols-from-cork"))
+        self.assertTrue(ArteTV.can_handle_url("http://www.arte.tv/guide/es/068380-000-A/una-noche-en-florencia"))
+        self.assertTrue(ArteTV.can_handle_url("http://www.arte.tv/guide/pl/068916-006-A/belle-and-sebastian-route-du-rock"))
+
+        # shouldn't match
+        self.assertFalse(ArteTV.can_handle_url("http://www.arte.tv/guide/fr/plus7/"))
+        self.assertFalse(ArteTV.can_handle_url("http://www.arte.tv/guide/de/plus7/"))
+        self.assertFalse(ArteTV.can_handle_url("http://www.tvcatchup.com/"))
+        self.assertFalse(ArteTV.can_handle_url("http://www.youtube.com/"))