Repository: streamlink/streamlink
Pull request #3375 (2020-11-28 14:13:09)

plugins.twitch: fix --twitch-disable-reruns
closes https://github.com/streamlink/streamlink/issues/3257

bastimeyer: Are you okay with these changes, @back-to ?

Patch:
diff --git a/src/streamlink/plugins/twitch.py b/src/streamlink/plugins/twitch.py
index b6804394..47895568 100644
--- a/src/streamlink/plugins/twitch.py
+++ b/src/streamlink/plugins/twitch.py
@@ -232,24 +232,6 @@ class TwitchAPI:
             validate.transform(int)
         ))
 
-    def stream_rerun(self, channel_id):
-        return self.call("/kraken/streams/{0}".format(channel_id), schema=validate.Schema(
-            {
-                "stream": validate.any(None, validate.all({
-                    "stream_type": validate.text,
-                    "broadcast_platform": validate.text,
-                    "channel": validate.all({
-                        "broadcaster_software": validate.text
-                    }, validate.get("broadcaster_software"))
-                }, validate.union((
-                    validate.get("stream_type"),
-                    validate.get("broadcast_platform"),
-                    validate.get("channel")
-                ))))
-            },
-            validate.get("stream")
-        ))
-
     def metadata_video(self, video_id):
         return self.call("/kraken/videos/{0}".format(video_id), schema=validate.Schema(validate.any(
             validate.all(
@@ -407,6 +389,35 @@ class TwitchAPI:
             validate.get("clip")
         ))
 
+    def stream_metadata(self, channel):
+        request = {
+            "operationName": "StreamMetadata",
+            "extensions": {
+                "persistedQuery": {
+                    "version": 1,
+                    "sha256Hash": "1c719a40e481453e5c48d9bb585d971b8b372f8ebb105b17076722264dfa5b3e"
+                }
+            },
+            "variables": {
+                "channelLogin": channel
+            }
+        }
+
+        return self.call_gql(request, schema=validate.Schema(
+            {
+                "data": {
+                    "user": {
+                        "stream": {
+                            "type": str,
+                        }
+                    }
+                }
+            },
+            validate.get("data"),
+            validate.get("user"),
+            validate.get("stream")
+        ))
+
 
 class Twitch(Plugin):
     arguments = PluginArguments(
@@ -595,8 +606,8 @@ class Twitch(Plugin):
             return False
 
         try:
-            stream_type, broadcast_platform, broadcaster_software = self.api.stream_rerun(self.channel_id)
-            if stream_type != "live" or broadcast_platform == "rerun" or broadcaster_software == "watch_party_rerun":
+            stream = self.api.stream_metadata(self.channel)
+            if stream["type"] != "live":
                 log.info("Reruns were disabled by command line option")
                 return True
         except (PluginError, TypeError):


Test patch:
diff --git a/tests/plugins/test_twitch.py b/tests/plugins/test_twitch.py
index fe7989f2..eb199178 100644
--- a/tests/plugins/test_twitch.py
+++ b/tests/plugins/test_twitch.py
@@ -470,22 +470,8 @@ class TestTwitchReruns(unittest.TestCase):
     log_call = call("Reruns were disabled by command line option")
 
     def subject(self, **params):
-        with requests_mock.Mocker() as mock:
-            mock.get(
-                "https://api.twitch.tv/kraken/users?login=foo",
-                json={"users": [{"_id": 1234}]}
-            )
-            mock.get(
-                "https://api.twitch.tv/kraken/streams/1234",
-                json={"stream": None} if params.pop("offline", False) else {"stream": {
-                    "stream_type": params.pop("stream_type", "live"),
-                    "broadcast_platform": params.pop("broadcast_platform", "live"),
-                    "channel": {
-                        "broadcaster_software": params.pop("broadcaster_software", "")
-                    }
-                }}
-            )
-
+        with patch("streamlink.plugins.twitch.TwitchAPI.stream_metadata") as mock:
+            mock.return_value = None if params.pop("offline", False) else {"type": params.pop("stream_type", "live")}
             session = Streamlink()
             Twitch.bind(session, "tests.plugins.test_twitch")
             plugin = Twitch("https://www.twitch.tv/foo")
@@ -501,18 +487,6 @@ class TestTwitchReruns(unittest.TestCase):
         self.assertTrue(self.subject(stream_type="rerun"))
         self.assertIn(self.log_call, mock_log.info.call_args_list)
 
-    def test_disable_reruns_mixed(self, mock_log):
-        self.assertTrue(self.subject(stream_type="rerun", broadcast_platform="live"))
-        self.assertIn(self.log_call, mock_log.info.call_args_list)
-
-    def test_disable_reruns_mixed2(self, mock_log):
-        self.assertTrue(self.subject(stream_type="live", broadcast_platform="rerun"))
-        self.assertIn(self.log_call, mock_log.info.call_args_list)
-
-    def test_disable_reruns_broadcaster_software(self, mock_log):
-        self.assertTrue(self.subject(broadcaster_software="watch_party_rerun"))
-        self.assertIn(self.log_call, mock_log.info.call_args_list)
-
     def test_disable_reruns_offline(self, mock_log):
         self.assertFalse(self.subject(offline=True))
         self.assertNotIn(self.log_call, mock_log.info.call_args_list)