Repository: streamlink/streamlink

Issue:
Media lookup error for specific Crunchyroll show only
----

### Checklist

- [x] This is a bug report.
- [ ] This is a feature request.
- [ ] This is a plugin (improvement) request.
- [x] I have read the contribution guidelines.

### Description

When I try to play any episode of Nekomonogatari Kuro, streamlink gives me a media lookup error.  

### Expected / Actual behavior

Fetch the available formats for any episode of Nekomonogatari Kuro from Crunchyroll.

/

```
[atemu@localhost ~]$ streamlink http://www.crunchyroll.com/nekomonogatari-black/episode-1-tsubasa-family-part-1-677667
[cli][info] Found matching plugin crunchyroll for URL http://www.crunchyroll.com/nekomonogatari-black/episode-1-tsubasa-family-part-1-677667
[plugin.crunchyroll][warning] No authentication provided, you won't be able to access premium restricted content
error: Media lookup error: You don't have permission to do this.
[atemu@localhost ~]$ streamlink http://www.crunchyroll.com/nekomonogatari-black/episode-2-tsubasa-family-part-2-677669
[cli][info] Found matching plugin crunchyroll for URL http://www.crunchyroll.com/nekomonogatari-black/episode-2-tsubasa-family-part-2-677669
[plugin.crunchyroll][warning] No authentication provided, you won't be able to access premium restricted content
error: Media lookup error: You don't have permission to do this.
[atemu@localhost ~]$ streamlink http://www.crunchyroll.com/nekomonogatari-black/episode-3-tsubasa-family-part-3-677671
[cli][info] Found matching plugin crunchyroll for URL http://www.crunchyroll.com/nekomonogatari-black/episode-3-tsubasa-family-part-3-677671
[plugin.crunchyroll][warning] No authentication provided, you won't be able to access premium restricted content
error: Media lookup error: You don't have permission to do this.
[atemu@localhost ~]$ streamlink http://www.crunchyroll.com/nekomonogatari-black/episode-4-tsubasa-family-part-4-677673
[cli][info] Found matching plugin crunchyroll for URL http://www.crunchyroll.com/nekomonogatari-black/episode-4-tsubasa-family-part-4-677673
[plugin.crunchyroll][warning] No authentication provided, you won't be able to access premium restricted content
error: Media lookup error: You don't have permission to do this.
```

### Reproduction steps / Explicit stream URLs to test

Be in the US (either physically or through VPN) or pass a US session ID to streamlink and use one of the following URLs:

1. http://www.crunchyroll.com/nekomonogatari-black/episode-1-tsubasa-family-part-1-677667
2. http://www.crunchyroll.com/nekomonogatari-black/episode-2-tsubasa-family-part-2-677669
3. http://www.crunchyroll.com/nekomonogatari-black/episode-3-tsubasa-family-part-3-677671
4. http://www.crunchyroll.com/nekomonogatari-black/episode-4-tsubasa-family-part-4-677673

### Log

```
[atemu@localhost ~]$ streamlink http://www.crunchyroll.com/nekomonogatari-black/episode-1-tsubasa-family-part-1-677667 -l debug
[cli][debug] OS:         Linux-3.4.113-lineageos-gbe73925-armv7l-with-arch
[cli][debug] Python:     3.6.4
[cli][debug] Streamlink: 0.11.0
[cli][debug] Requests(2.18.4), Socks(1.6.7), Websocket(0.47.0)
[cli][info] Found matching plugin crunchyroll for URL http://www.crunchyroll.com/nekomonogatari-black/episode-1-tsubasa-family-part-1-677667
[plugin.crunchyroll][debug] Creating session with locale: en_US
[plugin.crunchyroll][debug] Session created
[plugin.crunchyroll][warning] No authentication provided, you won't be able to access premium restricted content
error: Media lookup error: You don't have permission to do this.
```

### Comments, screenshots, etc.

The show is geo blocked in my country (DE), so I have to use a US session ID and that worked with every other geoblocked Crunchyroll show so far.   
I've also tried using a US VPN but that didn't make a difference either.

I have tested other geoblocked and non geoblocked Crunchyroll shows under the same conditions and they worked without any issues.

The show does not require CR Premium and plays in both the Official app and through youtube-dl.


Discussion:
I had a similar issue as yours recently. As strange as it might sound, it seems like changing the locale settings of my system from DE to US fixed the problem. What is yours set to? Might be worth a try.
My whole system uses en_US.UTF-8
Try the option `--locale en_US`, to be sure - it appears the locale is set correctly, but worth a go perhaps. 
It's giving me the same error unfortunately.

Do the links work for you? I want to make sure this isn't on my end.
Doesn't work for me with the same error. It's very odd to me that it's just this one series... I know we've seen issues with specific episodes in the past, but it's rarely been limited to a single set of episodes like this.
Is there a way to find out what exactly goes wrong?   
The error message isn't very specific and the debug output doesn't tell you anything either.
I don't have a crunchy roll account at the moment to test this out :(
@beardypig You don't need one, this is a publicly available video (I don't have one either).
Atleast the first link does work for me.
That said, I first tried with a command from my shell history, which gave me the same "Media lookup error". I had to get a new session id to get it working again, as the other one was a few days old and seems to be expired.

So, did you try using a new session id?

Edit: all 4 links work for me
I'm always generating a new session ID using the CR API, so that could a source of error, but those session IDs have worked for all other Geo blocked shows I've watched so far (including the previous seasons of this Anime) and letting streamlink create a new session by itself while I'm in the US through a VPN doesn't work either.
Some shows are very picky for some reason, you need to have a session ID generated from the US and access the stream from an IP in the US (doesn't have to be the same IP though). 
There does seem to be an issue with this particular show where it doesn't work with the session IDs generated by streamlink. 

Resolving patch:
diff --git a/src/streamlink/plugins/crunchyroll.py b/src/streamlink/plugins/crunchyroll.py
--- a/src/streamlink/plugins/crunchyroll.py
+++ b/src/streamlink/plugins/crunchyroll.py
@@ -1,8 +1,8 @@
 import argparse
+import datetime
 import random
 import re
 import string
-import datetime
 
 from streamlink.plugin import Plugin, PluginError, PluginArguments, PluginArgument
 from streamlink.plugin.api import http, validate
@@ -159,16 +159,11 @@ def start_session(self, device_id, **kwargs):
         Is recommended that you call this method before making any other calls
         to make sure you have a valid session against the server.
         """
-        params = {
-            "device_id": device_id,
-            "device_type": API_DEVICE_TYPE,
-            "access_token": API_ACCESS_TOKEN,
-        }
-
-        if self.auth:
-            params["auth"] = self.auth
-
-        return self._api_call("start_session", params, **kwargs)
+        if self.session_id:
+            http.cookies["sess_id"] = self.session_id
+        res = http.get("https://www.crunchyroll.com",
+                       headers={"User-Agent": API_USER_AGENT.format(self.locale)})
+        return res.cookies.get("sess_id", self.session_id)
 
     def login(self, username, password, **kwargs):
         """Authenticates the session to be able to access restricted data from
@@ -236,7 +231,12 @@ class Crunchyroll(Plugin):
             metavar="SESSION_ID",
             help="""
             Set a specific session ID for crunchyroll, can be used to bypass
-            region restrictions.
+            region restrictions. If using an authenticated session ID, it is
+            recommended that the authentication parameters be omitted as the
+            session ID is account specific.
+
+            Note: The session ID will be overwritten if authentication is used
+            and the session ID does not match the account.
             """
         ),
         # Deprecated, uses the general locale setting
@@ -331,15 +331,7 @@ def _create_api(self):
         )
 
         self.logger.debug("Creating session with locale: {0}", locale)
-        try:
-            api.session_id = api.start_session(device_id, schema=_session_schema)
-        except CrunchyrollAPIError as err:
-            if err.code == "bad_session":
-                self.logger.debug("Current session has expired, creating a new one")
-                api = CrunchyrollAPI(locale=locale)
-                api.session_id = api.start_session(device_id, schema=_session_schema)
-            else:
-                raise err
+        api.session_id = api.start_session(device_id, schema=_session_schema)
 
         # Save session and hope it lasts for a few hours
         self.cache.set("session_id", api.session_id, 4 * 60 * 60)
@@ -349,7 +341,7 @@ def _create_api(self):
             self.logger.debug("Using saved credentials")
         elif self.options.get("username"):
             try:
-                self.logger.info("Attempting to login using username and password")
+                self.logger.debug("Attempting to login using username and password")
                 login = api.login(
                     self.options.get("username"),
                     self.options.get("password"),
@@ -357,6 +349,11 @@ def _create_api(self):
                 )
                 api.auth = login["auth"]
 
+                user_session_id = api.start_session(device_id, schema=_session_schema)
+                if user_session_id != api.session_id:
+                    self.logger.warning("Session ID does not match account, resetting session ID")
+                    api.session_id = user_session_id
+
                 self.logger.info("Successfully logged in as '{0}'",
                                  login["user"]["username"] or login["user"]["email"])
 


Test patch: