Skip to content

Commit cba7ec7

Browse files
authored
Merge pull request #66 from anxdpanic/dev
2.0.9
2 parents 5fb9fa4 + 0b6193a commit cba7ec7

File tree

6 files changed

+75
-41
lines changed

6 files changed

+75
-41
lines changed

Diff for: addon.xml

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2-
<addon id="script.module.python.twitch" name="python-twitch for Kodi" version="2.0.8" provider-name="A Talented Community">
2+
<addon id="script.module.python.twitch" name="python-twitch for Kodi" version="2.0.9" provider-name="A Talented Community">
33
<requires>
44
<import addon="xbmc.python" version="2.20.0"/>
55
<import addon="script.module.six" version="1.11.0"/>
@@ -9,7 +9,9 @@
99
<extension point="xbmc.addon.metadata">
1010
<platform>all</platform>
1111
<news>
12-
[upd] Mark communities endpoints and queries as deprecated
12+
[upd] allow overriding headers for usher and hidden api calls
13+
[fix] decode byte responses in scraper
14+
[fix] usher/parser error handling
1315
</news>
1416
<assets>
1517
<icon>icon.png</icon>

Diff for: changelog.txt

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
2.0.9
2+
[upd] allow overriding headers for usher and hidden api calls
3+
[fix] decode byte responses in scraper
4+
[fix] usher/parser error handling
5+
16
2.0.8
27
[upd] Mark communities endpoints and queries as deprecated
38

Diff for: resources/lib/twitch/api/usher.py

+20-20
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ def valid_video_id(video_id):
3030

3131

3232
@query
33-
def channel_token(channel, platform=keys.WEB):
34-
q = HiddenApiQuery('channels/{channel}/access_token')
33+
def channel_token(channel, platform=keys.WEB, headers={}):
34+
q = HiddenApiQuery('channels/{channel}/access_token', headers=headers)
3535
q.add_urlkw(keys.CHANNEL, channel)
3636
q.add_param(keys.NEED_HTTPS, Boolean.TRUE)
3737
q.add_param(keys.PLATFORM, platform)
@@ -40,8 +40,8 @@ def channel_token(channel, platform=keys.WEB):
4040

4141

4242
@query
43-
def vod_token(video_id, platform=keys.WEB):
44-
q = HiddenApiQuery('vods/{vod}/access_token')
43+
def vod_token(video_id, platform=keys.WEB, headers={}):
44+
q = HiddenApiQuery('vods/{vod}/access_token', headers=headers)
4545
q.add_urlkw(keys.VOD, video_id)
4646
q.add_param(keys.NEED_HTTPS, Boolean.TRUE)
4747
q.add_param(keys.PLATFORM, platform)
@@ -56,12 +56,12 @@ def _legacy_video(video_id):
5656
return q
5757

5858

59-
def live_request(channel, platform=keys.WEB):
60-
token = channel_token(channel, platform=platform)
59+
def live_request(channel, platform=keys.WEB, headers={}):
60+
token = channel_token(channel, platform=platform, headers=headers)
6161
if keys.ERROR in token:
6262
return token
6363
else:
64-
q = UsherQuery('api/channel/hls/{channel}.m3u8')
64+
q = UsherQuery('api/channel/hls/{channel}.m3u8', headers=headers)
6565
q.add_urlkw(keys.CHANNEL, channel)
6666
q.add_param(keys.SIG, token[keys.SIG].encode('utf-8'))
6767
q.add_param(keys.TOKEN, token[keys.TOKEN].encode('utf-8'))
@@ -81,8 +81,8 @@ def live_request(channel, platform=keys.WEB):
8181

8282

8383
@query
84-
def _live(channel, token):
85-
q = UsherQuery('api/channel/hls/{channel}.m3u8')
84+
def _live(channel, token, headers={}):
85+
q = UsherQuery('api/channel/hls/{channel}.m3u8', headers=headers)
8686
q.add_urlkw(keys.CHANNEL, channel)
8787
q.add_param(keys.SIG, token[keys.SIG].encode('utf-8'))
8888
q.add_param(keys.TOKEN, token[keys.TOKEN].encode('utf-8'))
@@ -99,22 +99,22 @@ def _live(channel, token):
9999

100100

101101
@m3u8
102-
def live(channel, platform=keys.WEB):
103-
token = channel_token(channel, platform=platform)
102+
def live(channel, platform=keys.WEB, headers={}):
103+
token = channel_token(channel, platform=platform, headers=headers)
104104
if keys.ERROR in token:
105105
return token
106106
else:
107-
return _live(channel, token)
107+
return _live(channel, token, headers=headers)
108108

109109

110-
def video_request(video_id, platform=keys.WEB):
110+
def video_request(video_id, platform=keys.WEB, headers={}):
111111
video_id = valid_video_id(video_id)
112112
if video_id:
113-
token = vod_token(video_id, platform=platform)
113+
token = vod_token(video_id, platform=platform, headers=headers)
114114
if keys.ERROR in token:
115115
return token
116116
else:
117-
q = UsherQuery('vod/{id}')
117+
q = UsherQuery('vod/{id}', headers=headers)
118118
q.add_urlkw(keys.ID, video_id)
119119
q.add_param(keys.NAUTHSIG, token[keys.SIG].encode('utf-8'))
120120
q.add_param(keys.NAUTH, token[keys.TOKEN].encode('utf-8'))
@@ -137,8 +137,8 @@ def video_request(video_id, platform=keys.WEB):
137137

138138

139139
@query
140-
def _vod(video_id, token):
141-
q = UsherQuery('vod/{id}')
140+
def _vod(video_id, token, headers={}):
141+
q = UsherQuery('vod/{id}', headers=headers)
142142
q.add_urlkw(keys.ID, video_id)
143143
q.add_param(keys.NAUTHSIG, token[keys.SIG].encode('utf-8'))
144144
q.add_param(keys.NAUTH, token[keys.TOKEN].encode('utf-8'))
@@ -156,14 +156,14 @@ def _vod(video_id, token):
156156

157157

158158
@m3u8
159-
def video(video_id, platform=keys.WEB):
159+
def video(video_id, platform=keys.WEB, headers={}):
160160
video_id = valid_video_id(video_id)
161161
if video_id:
162-
token = vod_token(video_id, platform=platform)
162+
token = vod_token(video_id, platform=platform, headers=headers)
163163
if keys.ERROR in token:
164164
return token
165165
else:
166-
return _vod(video_id, token)
166+
return _vod(video_id, token, headers=headers)
167167
else:
168168
raise NotImplementedError('Unknown Video Type')
169169

Diff for: resources/lib/twitch/parser.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ def _find_frame_rate(group_id, group_name):
4848
def m3u8(f):
4949
def m3u8_wrapper(*args, **kwargs):
5050
results = f(*args, **kwargs)
51-
results = results.decode('utf-8')
51+
try:
52+
results = results.decode('utf-8')
53+
except AttributeError:
54+
pass
5255
if keys.ERROR in results:
5356
if isinstance(results, dict):
5457
return results

Diff for: resources/lib/twitch/queries.py

+39-18
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
See LICENSES/GPL-3.0-only for more information.
1111
"""
1212

13+
from copy import deepcopy
14+
1315
from six.moves.urllib.parse import urljoin
1416

1517
from . import CLIENT_ID, OAUTH_TOKEN, APP_TOKEN
@@ -122,21 +124,23 @@ def execute(self):
122124

123125
class ApiQuery(JsonQuery):
124126
def __init__(self, path, headers={}, data={}, use_token=True, method=methods.GET):
125-
headers.setdefault('Client-ID', CLIENT_ID)
127+
_headers = deepcopy(headers)
128+
_headers.setdefault('Client-ID', CLIENT_ID)
126129
if use_token and OAUTH_TOKEN:
127-
headers.setdefault('Authorization', 'OAuth {access_token}'.format(access_token=OAUTH_TOKEN))
128-
super(ApiQuery, self).__init__(_kraken_baseurl, headers, data, method)
130+
_headers.setdefault('Authorization', 'OAuth {access_token}'.format(access_token=OAUTH_TOKEN))
131+
super(ApiQuery, self).__init__(_kraken_baseurl, _headers, data, method)
129132
self.add_path(path)
130133

131134

132135
class HelixApiQuery(HelixJsonQuery):
133136
def __init__(self, path, headers={}, data={}, use_app_token=False, method=methods.GET):
134-
headers.setdefault('Client-ID', CLIENT_ID)
137+
_headers = deepcopy(headers)
138+
_headers.setdefault('Client-ID', CLIENT_ID)
135139
if use_app_token and APP_TOKEN:
136-
headers.setdefault('Authorization', 'Bearer {access_token}'.format(access_token=APP_TOKEN))
140+
_headers.setdefault('Authorization', 'Bearer {access_token}'.format(access_token=APP_TOKEN))
137141
elif OAUTH_TOKEN:
138-
headers.setdefault('Authorization', 'Bearer {access_token}'.format(access_token=OAUTH_TOKEN))
139-
super(HelixApiQuery, self).__init__(_helix_baseurl, headers, data, method)
142+
_headers.setdefault('Authorization', 'Bearer {access_token}'.format(access_token=OAUTH_TOKEN))
143+
super(HelixApiQuery, self).__init__(_helix_baseurl, _headers, data, method)
140144
self._params = list()
141145
self.add_path(path)
142146

@@ -154,37 +158,54 @@ def add_param(self, key, value, default=None):
154158

155159
class HiddenApiQuery(JsonQuery):
156160
def __init__(self, path, headers={}, data={}, use_token=True, method=methods.GET):
157-
headers.setdefault('Client-ID', CLIENT_ID)
158-
if use_token and OAUTH_TOKEN:
159-
headers.setdefault('Authorization', 'OAuth {access_token}'.format(access_token=OAUTH_TOKEN))
160-
super(HiddenApiQuery, self).__init__(_hidden_baseurl, headers, data, method)
161+
_headers = deepcopy(headers)
162+
if 'Client-ID' not in _headers:
163+
_headers.setdefault('Client-ID', CLIENT_ID)
164+
if 'Client-ID' in _headers and not _headers.get('Client-ID'):
165+
del _headers['Client-ID']
166+
if 'Authorization' not in _headers:
167+
if use_token and OAUTH_TOKEN:
168+
_headers.setdefault('Authorization', 'OAuth {access_token}'.format(access_token=OAUTH_TOKEN))
169+
if 'Authorization' in _headers and not _headers.get('Authorization'):
170+
del _headers['Authorization']
171+
super(HiddenApiQuery, self).__init__(_hidden_baseurl, _headers, data, method)
161172
self.add_path(path)
162173

163174

164175
class UsherQuery(DownloadQuery):
165176
def __init__(self, path, headers={}, data={}, method=methods.GET):
166-
headers.setdefault('Client-ID', CLIENT_ID)
167-
if OAUTH_TOKEN:
168-
headers.setdefault('Authorization', 'OAuth {access_token}'.format(access_token=OAUTH_TOKEN))
169-
super(UsherQuery, self).__init__(_usher_baseurl, headers, data, method)
177+
_headers = deepcopy(headers)
178+
if 'Client-ID' not in _headers:
179+
_headers.setdefault('Client-ID', CLIENT_ID)
180+
if 'Client-ID' in _headers and not _headers.get('Client-ID'):
181+
del _headers['Client-ID']
182+
if 'Authorization' not in _headers:
183+
if OAUTH_TOKEN:
184+
_headers.setdefault('Authorization', 'OAuth {access_token}'.format(access_token=OAUTH_TOKEN))
185+
if 'Authorization' in _headers and not _headers.get('Authorization'):
186+
del _headers['Authorization']
187+
super(UsherQuery, self).__init__(_usher_baseurl, _headers, data, method)
170188
self.add_path(path)
171189

172190

173191
class OAuthQuery(JsonQuery):
174192
def __init__(self, path, headers={}, data={}, method=methods.GET):
175-
super(JsonQuery, self).__init__(_oauth_baseurl, headers, data, method)
193+
_headers = deepcopy(headers)
194+
super(JsonQuery, self).__init__(_oauth_baseurl, _headers, data, method)
176195
self.add_path(path)
177196

178197

179198
class ClipsQuery(DownloadQuery):
180199
def __init__(self, path, headers={}, data={}, method=methods.GET):
181-
super(ClipsQuery, self).__init__(_clips_baseurl, headers, data, method)
200+
_headers = deepcopy(headers)
201+
super(ClipsQuery, self).__init__(_clips_baseurl, _headers, data, method)
182202
self.add_path(path)
183203

184204

185205
class UploadsQuery(DownloadQuery):
186206
def __init__(self, path, headers={}, data={}, method=methods.PUT):
187-
super(UploadsQuery, self).__init__(_uploads_baseurl, headers, data, method)
207+
_headers = deepcopy(headers)
208+
super(UploadsQuery, self).__init__(_uploads_baseurl, _headers, data, method)
188209
self.add_path(path)
189210

190211

Diff for: resources/lib/twitch/scraper.py

+3
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ def download(baseurl, parameters={}, headers={}, data={}, method=methods.GET, re
108108
else:
109109
raise ResourceUnavailableException('Max retries exceeded')
110110

111+
if isinstance(content, bytes):
112+
content = content.decode('utf-8')
113+
111114
if not response_headers:
112115
return content
113116
else:

0 commit comments

Comments
 (0)