Skip to content

Commit 0c89f65

Browse files
authored
Merge pull request #17 from anxdpanic/dev
add video upload end-points
2 parents e541e76 + 270101b commit 0c89f65

File tree

6 files changed

+83
-7
lines changed

6 files changed

+83
-7
lines changed

addon.xml

Lines changed: 1 addition & 1 deletion
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="1.0.0~alpha9" provider-name="A Talented Community">
2+
<addon id="script.module.python.twitch" name="python-twitch for Kodi" version="1.0.0~beta1" provider-name="A Talented Community">
33
<requires>
44
<import addon="xbmc.python" version="2.1.0"/>
55
<import addon="script.module.six" version="1.9.0"/>

resources/lib/twitch/api/v5/videos.py

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
# -*- encoding: utf-8 -*-
22
# https://dev.twitch.tv/docs/v5/reference/videos/
33

4-
from twitch import keys
5-
from twitch.api.parameters import BroadcastType, Period
4+
from twitch import keys, methods
5+
from twitch.api.parameters import BroadcastType, Period, Language
66
from twitch.queries import V5Query as Qry
77
from twitch.queries import HiddenApiQuery as HQry
8+
from twitch.queries import UploadsQuery as UQry
89
from twitch.queries import query
910

1011

@@ -38,6 +39,63 @@ def get_followed(limit=10, offset=0, broadcast_type=BroadcastType.HIGHLIGHT):
3839
return q
3940

4041

42+
# required scope: channel_editor
43+
@query
44+
def create(channel_id, title, description=None, game=None, language=None, tag_list=None):
45+
q = Qry('videos/', method=methods.POST)
46+
q.add_param(keys.CHANNEL_ID, channel_id)
47+
q.add_param(keys.TITLE, title)
48+
q.add_param(keys.DESCRIPTION, description)
49+
q.add_param(keys.GAME, game)
50+
if language is not None:
51+
q.add_param(keys.LANGUAGE, Language.validate(language))
52+
q.add_param(keys.TAG_LIST, tag_list)
53+
return q
54+
55+
56+
# required scope: channel_editor
57+
@query
58+
def update(video_id, title=None, description=None, game=None, language=None, tag_list=None):
59+
q = Qry('videos/{video_id}', method=methods.PUT)
60+
q.add_urlkw(keys.VIDEO_ID, video_id)
61+
q.add_param(keys.TITLE, title)
62+
q.add_param(keys.DESCRIPTION, description)
63+
q.add_param(keys.GAME, game)
64+
if language is not None:
65+
q.add_param(keys.LANGUAGE, Language.validate(language))
66+
q.add_param(keys.TAG_LIST, tag_list)
67+
return q
68+
69+
70+
# required scope: channel_editor
71+
@query
72+
def delete(video_id):
73+
q = Qry('videos/{video_id}', method=methods.DELETE)
74+
q.add_urlkw(keys.VIDEO_ID, video_id)
75+
return q
76+
77+
78+
# requires upload token
79+
@query
80+
def upload_part(video_id, part, upload_token, content_length, data):
81+
q = UQry('upload/{video_id}', method=methods.PUT)
82+
q.set_headers({'Content-Length': content_length, 'Content-Type': 'application/octet-stream'})
83+
q.add_urlkw(keys.VIDEO_ID, video_id)
84+
q.add_param(keys.PART, part)
85+
q.add_param(keys.UPLOAD_TOKEN, upload_token)
86+
q.add_bin(data)
87+
return q
88+
89+
90+
# requires upload token
91+
@query
92+
def complete_upload(video_id, upload_token):
93+
q = UQry('upload/{video_id}/complete', method=methods.POST)
94+
q.add_urlkw(keys.VIDEO_ID, video_id)
95+
q.add_param(keys.UPLOAD_TOKEN, upload_token)
96+
return q
97+
98+
4199
# required scope: none
42100
# undocumented / unsupported
43101
@query

resources/lib/twitch/keys.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
NEED_HTTPS = 'need_https'
5252
NOTIFICATIONS = 'notifications'
5353
OFFSET = 'offset'
54+
PART = 'part'
5455
PERIOD = 'period'
5556
POSITION = 'position'
5657
POST_ID = 'post_id'
@@ -66,6 +67,7 @@
6667
STATUS = 'status'
6768
STREAM_TYPE = 'stream_type'
6869
SUMMARY = 'summary'
70+
TAG_LIST = 'tag_list'
6971
TARGET_ID = 'target_id'
7072
TEAM = 'team'
7173
TITLE = 'title'
@@ -78,6 +80,7 @@
7880
USER_AGENT_STRING = ('Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) '
7981
'Gecko/20100101 Firefox/6.0')
8082
USER_ID = 'user_id'
83+
UPLOAD_TOKEN = 'upload_token'
8184
VIDEO_ID = 'video_id'
8285
VOD = 'vod'
8386
XBOX_HEARTBEAT = 'xbox_heartbeat'

resources/lib/twitch/parser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def m3u8_wrapper(*args, **kwargs):
2525
else:
2626
error = re.search(_error_pattern, results)
2727
if error:
28-
return {'error': 'Error', 'message': error.group('message'), 'status': 0}
28+
return {'error': 'Error', 'message': error.group('message'), 'status': 404}
2929
return m3u8_to_list(results)
3030

3131
return m3u8_wrapper

resources/lib/twitch/queries.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
_hidden_baseurl = 'https://api.twitch.tv/api/'
1313
_usher_baseurl = 'https://usher.ttvnw.net/'
1414
_clips_baseurl = 'https://clips.twitch.tv/'
15+
_uploads_baseurl = 'https://uploads.twitch.tv/'
1516

1617
_v4_headers = {'ACCEPT': 'application/vnd.twitchtv.v4+json'}
1718
_v5_headers = {'ACCEPT': 'application/vnd.twitchtv.v5+json'}
@@ -62,6 +63,10 @@ def add_data(self, key, value, default=None):
6263
self._data[key] = value
6364
return self
6465

66+
def add_bin(self, data):
67+
self._data = data
68+
return self
69+
6570
def add_param(self, key, value, default=None):
6671
assert_new(self._params, key)
6772
if value != default:
@@ -73,8 +78,12 @@ def add_urlkw(self, kw, replacement):
7378
self._urlkws[kw] = replacement
7479
return self
7580

81+
def set_headers(self, headers):
82+
self._headers = headers
83+
return self
84+
7685
def __str__(self):
77-
return '{method} Query to {url}, params {params}, data {data}, headers {headers}'\
86+
return '{method} Query to {url}, params {params}, data {data}, headers {headers}' \
7887
.format(url=self.url, params=self.params, headers=self.headers, data=self.data, method=self.method)
7988

8089
def execute(self, f):
@@ -129,6 +138,12 @@ def __init__(self, path, headers={}, data={}, method=methods.GET):
129138
self.add_path(path)
130139

131140

141+
class UploadsQuery(DownloadQuery):
142+
def __init__(self, path, headers={}, data={}, method=methods.PUT):
143+
super(UploadsQuery, self).__init__(_uploads_baseurl, headers, data, method)
144+
self.add_path(path)
145+
146+
132147
class V4Query(ApiQuery):
133148
def __init__(self, path, method=methods.GET):
134149
super(V4Query, self).__init__(path, _v4_headers, method=method)

resources/lib/twitch/scraper.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ def download(baseurl, parameters={}, headers={}, data={}, method=methods.GET):
5353
headers.update({USER_AGENT: USER_AGENT_STRING})
5454
response = requests.request(method=method, url=url, headers=headers, data=data)
5555
content = response.content
56-
if not content and response.status_code == 204:
57-
content = '{"status": 204}'
56+
if not content:
57+
content = '{"status": %d}' % response.status_code
5858
break
5959
except Exception as err:
6060
if not isinstance(err, URLError):

0 commit comments

Comments
 (0)