Repository: streamlink/streamlink
Pull request #2565 (2019-07-27 18:58:15)

plugins.youtube: fixed reason msg, updated _url_re
closes #2557


Patch:
diff --git a/src/streamlink/plugins/youtube.py b/src/streamlink/plugins/youtube.py
index b8c9b3e9..9d7d3ffe 100644
--- a/src/streamlink/plugins/youtube.py
+++ b/src/streamlink/plugins/youtube.py
@@ -85,8 +85,13 @@ _config_schema = validate.Schema(
                 },
                 validate.optional("videoDetails"): {
                     validate.optional("isLive"): validate.transform(bool),
-                }
-            }
+                },
+                validate.optional("playabilityStatus"): {
+                    validate.optional("status"): validate.text,
+                    validate.optional("reason"): validate.all(validate.text,
+                                                              validate.transform(maybe_decode)),
+                },
+            },
         ),
         validate.optional("live_playback"): validate.transform(bool),
         validate.optional("reason"): validate.all(validate.text, validate.transform(maybe_decode)),
@@ -104,20 +109,25 @@ _ytdata_re = re.compile(r'window\["ytInitialData"\]\s*=\s*({.*?});', re.DOTALL)
 _url_re = re.compile(r"""(?x)https?://(?:\w+\.)?youtube\.com
     (?:
         (?:
-            /(?:watch.+v=|embed/(?!live_stream)|v/)
-            (?P<video_id>[0-9A-z_-]{11})
+            /(?:
+                watch.+v=
+                |
+                embed/(?!live_stream)
+                |
+                v/
+            )(?P<video_id>[0-9A-z_-]{11})
         )
         |
         (?:
             /(?:
-                (?:user|channel)/
+                (?:user|c(?:hannel)?)/
                 |
                 embed/live_stream\?channel=
-            )(?P<user>[^/?&]+)
+            )[^/?&]+
         )
         |
         (?:
-            /(?:c/)?(?P<liveChannel>[^/?]+)/live/?$
+            /(?:c/)?[^/?]+/live/?$
         )
     )
 """)
@@ -316,9 +326,12 @@ class YouTube(Plugin):
 
             res = self.session.http.get(self._video_info_url, params=params)
             info_parsed = parse_query(res.content if is_py2 else res.text, name="config", schema=_config_schema)
-            if info_parsed.get("status") == "fail":
+            if (info_parsed.get("player_response", {}).get("playabilityStatus", {}).get("status") != "OK"
+                    or info_parsed.get("status") == "fail"):
+                reason = (info_parsed.get("player_response", {}).get("playabilityStatus", {}).get("reason")
+                          or info_parsed.get("reason"))
                 log.debug("get_video_info - {0}: {1}".format(
-                    count, info_parsed.get("reason"))
+                    count, reason)
                 )
                 continue
             self.author = info_parsed.get("author")


Test patch:
diff --git a/tests/plugins/test_youtube.py b/tests/plugins/test_youtube.py
index d6e1ab60..2cf454ed 100644
--- a/tests/plugins/test_youtube.py
+++ b/tests/plugins/test_youtube.py
@@ -6,53 +6,39 @@ from streamlink.plugins.youtube import YouTube, _url_re
 class TestPluginYouTube(unittest.TestCase):
     def test_can_handle_url(self):
         should_match = [
+            "https://www.youtube.com/EXAMPLE/live",
+            "https://www.youtube.com/c/EXAMPLE",
+            "https://www.youtube.com/c/EXAMPLE/",
             "https://www.youtube.com/c/EXAMPLE/live",
             "https://www.youtube.com/c/EXAMPLE/live/",
             "https://www.youtube.com/channel/EXAMPLE",
-            "https://www.youtube.com/v/aqz-KE-bpKQ",
+            "https://www.youtube.com/channel/EXAMPLE/",
             "https://www.youtube.com/embed/aqz-KE-bpKQ",
+            "https://www.youtube.com/embed/live_stream?channel=UCNye-wNBqNL5ZzHSJj3l8Bg",
+            "https://www.youtube.com/user/EXAMPLE",
             "https://www.youtube.com/user/EXAMPLE/",
+            "https://www.youtube.com/user/EXAMPLE/live",
+            "https://www.youtube.com/v/aqz-KE-bpKQ",
             "https://www.youtube.com/watch?v=aqz-KE-bpKQ",
-            "https://www.youtube.com/embed/live_stream?channel=UCNye-wNBqNL5ZzHSJj3l8Bg",
         ]
         for url in should_match:
-            self.assertTrue(YouTube.can_handle_url(url))
+            self.assertTrue(YouTube.can_handle_url(url), url)
 
         should_not_match = [
+            "https://accounts.google.com/",
             "https://www.youtube.com",
+            "https://www.youtube.com/account",
+            "https://www.youtube.com/feed/guide_builder",
+            "https://www.youtube.com/t/terms",
         ]
         for url in should_not_match:
-            self.assertFalse(YouTube.can_handle_url(url))
+            self.assertFalse(YouTube.can_handle_url(url), url)
 
     def _test_regex(self, url, expected_string, expected_group):
         m = _url_re.match(url)
         self.assertIsNotNone(m)
         self.assertEqual(expected_string, m.group(expected_group))
 
-    def test_regex_liveChannel_c(self):
-        self._test_regex("https://www.youtube.com/c/EXAMPLE/live",
-                         "EXAMPLE", "liveChannel")
-
-    def test_regex_liveChannel_no_c(self):
-        self._test_regex("https://www.youtube.com/EXAMPLE1/live",
-                         "EXAMPLE1", "liveChannel")
-
-    def test_regex_user_channel(self):
-        self._test_regex("https://www.youtube.com/channel/EXAMPLE2",
-                         "EXAMPLE2", "user")
-
-    def test_regex_user_user(self):
-        self._test_regex("https://www.youtube.com/channel/EXAMPLE3",
-                         "EXAMPLE3", "user")
-
-    def test_regex_user_embed_list_stream(self):
-        self._test_regex("https://www.youtube.com/embed/live_stream?channel=UCNye-wNBqNL5ZzHSJj3l8Bg",
-                         "UCNye-wNBqNL5ZzHSJj3l8Bg", "user")
-
-    def test_regex_user_embed_list_stream_2(self):
-        self._test_regex("https://www.youtube.com/embed/live_stream?channel=UCNye-wNBqNL5ZzHSJj3l8Bg&autoplay=1&modestbranding=1&rel=0&showinfo=0&color=white&fs=1",
-                         "UCNye-wNBqNL5ZzHSJj3l8Bg", "user")
-
     def test_regex_video_id_v(self):
         self._test_regex("https://www.youtube.com/v/aqz-KE-bpKQ",
                          "aqz-KE-bpKQ", "video_id")