Repository: streamlink/streamlink

Issue:
Zattoo (403 Client Error)
<!--
Thanks for reporting a plugin issue!
USE THE TEMPLATE. Otherwise your plugin issue may be rejected.

First, see the contribution guidelines:
https://github.com/streamlink/streamlink/blob/master/CONTRIBUTING.md#contributing-to-streamlink

Also check the list of open and closed plugin issues:
https://github.com/streamlink/streamlink/issues?q=is%3Aissue+label%3A%22plugin+issue%22

Please see the text preview to avoid unnecessary formatting errors.
-->


## Plugin Issue

<!-- Replace [ ] with [x] in order to check the box -->
- [x] This is a plugin issue and I have read the contribution guidelines.


### Description

<!-- Explain the plugin issue as thoroughly as you can. -->


### Reproduction steps / Explicit stream URLs to test

<!-- How can we reproduce this? Please note the exact steps below using the list format supplied. If you need more steps please add them. -->

1. streamlink -o "RTS Un SD.ts" https://zattoo.com/watch/rts_un 1500k --zattoo-email xxx@xxx.xxx --zattoo-password xxx


### Log output

<!--
TEXT LOG OUTPUT IS REQUIRED for a plugin issue!
Use the `--loglevel debug` parameter and avoid using parameters which suppress log output.
https://streamlink.github.io/cli.html#cmdoption-l

Make sure to **remove usernames and passwords**
You can copy the output to https://gist.github.com/ or paste it below.
-->

```
[plugin.zattoo][debug] Restored cookies: zattoo.session, pzuid, beaker.session.id
[cli][debug] OS:         Linux-4.19.0-0.bpo.6-amd64-x86_64-with-debian-9.13
[cli][debug] Python:     3.5.3
[cli][debug] Streamlink: 1.5.0
[cli][debug] Requests(2.24.0), Socks(1.7.1), Websocket(0.57.0)
[cli][info] Found matching plugin zattoo for URL https://zattoo.com/watch/rts_un
[cli][debug] Plugin specific arguments:
[cli][debug]  --zattoo-email=********@********.******** (email)
[cli][debug]  --zattoo-password=******** (password)
[cli][debug]  --zattoo-stream-types=['hls'] (stream_types)
[plugin.zattoo][debug] Session control for zattoo.com
[plugin.zattoo][debug] User is not logged in
[plugin.zattoo][debug] _hello ...
error: Unable to open URL: https://zattoo.com/zapi/session/hello (403 Client Error: Forbidden for url: https://zatt
oo.com/zapi/session/hello)
[1]    15604 exit 1     streamlink -o "RTS Un SD.ts" https://zattoo.com/watch/rts_un 1500
```


### Additional comments, screenshots, etc.



[Love Streamlink? Please consider supporting our collective. Thanks!](https://opencollective.com/streamlink/donate)



Discussion:
Had the same problem yesterday, Zattoo had changed the hello API.

I changed the following to get it running again:


The following variable changed:
```python
API_HELLO = '{0}/zapi/v2/session/hello'
```

This changed in the hello function:
```python
res_apptid = self.session.http.get('https://zattoo.com/client/token-2fb69f883fea03d06c68c6e5f21ddaea.json')
obj_apptid = json.loads(res_apptid.text)
log.debug(obj_apptid['app_tid'])


params = {
  'uuid': __uuid,
  'app_tid': obj_apptid['app_tid'],
  'app_version': '1.0.0'
}
```

Ok, thank you for the information, on the other hand possible to have the zattoo.py file attached to update it?
Submit a pull request, so it can be reviewed

Resolving patch:
diff --git a/src/streamlink/plugins/zattoo.py b/src/streamlink/plugins/zattoo.py
--- a/src/streamlink/plugins/zattoo.py
+++ b/src/streamlink/plugins/zattoo.py
@@ -19,6 +19,7 @@
 class Zattoo(Plugin):
     API_CHANNELS = '{0}/zapi/v2/cached/channels/{1}?details=False'
     API_HELLO = '{0}/zapi/session/hello'
+    API_HELLO_V2 = '{0}/zapi/v2/session/hello'
     API_HELLO_V3 = '{0}/zapi/v3/session/hello'
     API_LOGIN = '{0}/zapi/v2/account/login'
     API_LOGIN_V3 = '{0}/zapi/v3/account/login'
@@ -158,18 +159,21 @@ def _hello(self):
         # a new session is required for the app_token
         self.session.http.cookies = cookiejar_from_dict({})
         if self.base_url == 'https://zattoo.com':
-            app_token_url = 'https://zattoo.com/int/'
+            app_token_url = 'https://zattoo.com/client/token-2fb69f883fea03d06c68c6e5f21ddaea.json'
         elif self.base_url == 'https://www.quantum-tv.com':
             app_token_url = 'https://www.quantum-tv.com/token-4d0d61d4ce0bf8d9982171f349d19f34.json'
         else:
             app_token_url = self.base_url
-        res = self.session.http.get(app_token_url)
-        match = self._app_token_re.search(res.text)
 
+        res = self.session.http.get(app_token_url)
         if self.base_url == 'https://www.quantum-tv.com':
             app_token = self.session.http.json(res)["session_token"]
             hello_url = self.API_HELLO_V3.format(self.base_url)
+        elif self.base_url == 'https://zattoo.com':
+            app_token = self.session.http.json(res)['app_tid']
+            hello_url = self.API_HELLO_V2.format(self.base_url)
         else:
+            match = self._app_token_re.search(res.text)
             app_token = match.group(1)
             hello_url = self.API_HELLO.format(self.base_url)
 
@@ -180,10 +184,17 @@ def _hello(self):
             self._session_attributes.set(
                 'uuid', __uuid, expires=self.TIME_SESSION)
 
-        params = {
-            'client_app_token': app_token,
-            'uuid': __uuid,
-        }
+        if self.base_url == 'https://zattoo.com':
+            params = {
+                'uuid': __uuid,
+                'app_tid': app_token,
+                'app_version': '1.0.0'
+            }
+        else:
+            params = {
+                'client_app_token': app_token,
+                'uuid': __uuid,
+            }
 
         if self.base_url == 'https://www.quantum-tv.com':
             params['app_version'] = '3.2028.3'


Test patch: