Repository: streamlink/streamlink

Issue:
Twitch Videos do not work
- [x] This is a bug report.


### Description
Streamlink cannot parse these kind of URLs form Twitch Viedeos / past broadcasts
example: http://player.twitch.tv/?video=v133805360

### Expected / Actual behavior
Opening VLC player and running the streamvideo

### Reproduction steps / Stream URLs to test
streamlink http://player.twitch.tv/?video=v133805360 best

### Environment details (operating system, python version, etc.)
Win7 SP1 x64, VLC 2.2.4, Streamlink 0.5.0

### logs
error: Unable to open URL: https://api.twitch.tv/kraken/users.json (400 Client Error: Bad Request for url: https://api.twitch.tv/kraken/users.json?as3=t&oauth_t
oken=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&login=%3Fvideo%3Dv133805360)

_token was hidden_



Discussion:
Was this a token generated by `livestreamer` or are you a new user? If you were using `livestreamer` before you may have an old token - try updating your token using:
```
streamlink --twitch-oauth-authenticate
```
And then follow the instructions on streamlink documentation page you are redirected to. 
The issue here is that the URL is being interpreted as a regular stream URL (see the login parameter: `login=%3Fvideo%3Dv133805360`).

The VOD URL from above is from Twitch's "popout player" window. The regular URLs work flawlessly:
https://www.twitch.tv/videos/133805360

Resolving patch:
diff --git a/src/streamlink/plugins/twitch.py b/src/streamlink/plugins/twitch.py
--- a/src/streamlink/plugins/twitch.py
+++ b/src/streamlink/plugins/twitch.py
@@ -247,7 +247,7 @@ def hosted_channel(self, **params):
         return self.call_subdomain("tmi", "/hosts", format="", **params)
 
     def clip_status(self, channel, clip_name, schema):
-        return http.json(self.call_subdomain("clips", "/api/v1/clips/" + channel + "/" + clip_name + "/status", format=""), schema=schema)
+        return http.json(self.call_subdomain("clips", "/api/v2/clips/" + clip_name + "/status", format=""), schema=schema)
 
     # Unsupported/Removed private API calls
 
@@ -282,19 +282,36 @@ def can_handle_url(cls, url):
 
     def __init__(self, url):
         Plugin.__init__(self, url)
-        match = _url_re.match(url).groupdict()
-        self._channel = match.get("channel") and match.get("channel").lower()
-        self._channel_id = None
-        self.subdomain = match.get("subdomain")
-        self.video_type = match.get("video_type")
-        if match.get("videos_id"):
-            self.video_type = "v"
-        self.video_id = match.get("video_id") or match.get("videos_id")
-        self.clip_name = match.get("clip_name")
         self._hosted_chain = []
-
+        match = _url_re.match(url).groupdict()
         parsed = urlparse(url)
         self.params = parse_query(parsed.query)
+        self.subdomain = match.get("subdomain")
+        self.video_id = None
+        self.video_type = None
+        self._channel_id = None
+        self._channel = None
+        self.clip_name = None
+
+        if self.subdomain == "player":
+            # pop-out player
+            if self.params.get("video"):
+                try:
+                    self.video_type = self.params["video"][0]
+                    self.video_id = self.params["video"][1:]
+                except IndexError:
+                    self.logger.debug("Invalid video param: {0}", self.params["video"])
+            self._channel = self.params.get("channel")
+        elif self.subdomain == "clips":
+            # clip share URL
+            self.clip_name = match.get("channel")
+        else:
+            self._channel = match.get("channel") and match.get("channel").lower()
+            self.video_type = match.get("video_type")
+            if match.get("videos_id"):
+                self.video_type = "v"
+            self.video_id = match.get("video_id") or match.get("videos_id")
+            self.clip_name = match.get("clip_name")
 
         self.api = TwitchAPI(beta=self.subdomain == "beta", version=5)
         self.usher = UsherService()
@@ -596,7 +613,7 @@ def _get_streams(self):
                 return self._get_video_streams()
         elif self.clip_name:
             return self._get_clips()
-        else:
+        elif self._channel:
             return self._get_hls_streams("live")
 
 


Test patch: