Commit | Line | Data |
---|---|---|
56d5cd36 | 1 | import os |
2 | import sys | |
3 | import time | |
4 | ||
5 | import json | |
6 | import requests | |
7 | from requests_oauthlib import OAuth1 | |
8 | ||
9 | ||
10 | MEDIA_ENDPOINT_URL = 'https://upload.twitter.com/1.1/media/upload.json' | |
11 | POST_TWEET_URL = 'https://api.twitter.com/1.1/statuses/update.json' | |
12 | ||
d3ac7c48 IK |
13 | CONSUMER_KEY = 'hJHyPhuU7nSVHrKTVsGVDM4Lw' |
14 | CONSUMER_SECRET = '8UCYePqc1y9DY6mg0yQzLoTuq57AIysype2Si63714uACGMCbO' | |
56d5cd36 | 15 | |
587588c6 | 16 | with open(os.path.expanduser('~') + '/.rainbow_oauth') as fp: |
d3ac7c48 IK |
17 | ACCESS_TOKEN = fp.readline().rstrip('\n') |
18 | ACCESS_TOKEN_SECRET = fp.readline().rstrip('\n') | |
56d5cd36 | 19 | |
20 | oauth = OAuth1(CONSUMER_KEY, | |
21 | client_secret=CONSUMER_SECRET, | |
22 | resource_owner_key=ACCESS_TOKEN, | |
23 | resource_owner_secret=ACCESS_TOKEN_SECRET) | |
24 | ||
25 | ||
26 | class VideoTweet(object): | |
27 | ||
28 | def __init__(self, file_name): | |
29 | ''' | |
30 | Defines video tweet properties | |
31 | ''' | |
32 | self.video_filename = file_name | |
33 | self.total_bytes = os.path.getsize(self.video_filename) | |
34 | self.media_id = None | |
35 | self.processing_info = None | |
36 | ||
37 | ||
38 | def upload_init(self): | |
39 | ''' | |
40 | Initializes Upload | |
41 | ''' | |
42 | print('INIT') | |
43 | ||
44 | request_data = { | |
45 | 'command': 'INIT', | |
46 | 'media_type': 'video/mp4', | |
47 | 'total_bytes': self.total_bytes, | |
52ea40aa | 48 | 'media_category': 'tweet_video' |
56d5cd36 | 49 | } |
50 | ||
51 | req = requests.post(url=MEDIA_ENDPOINT_URL, data=request_data, auth=oauth) | |
52 | media_id = req.json()['media_id'] | |
53 | ||
54 | self.media_id = media_id | |
55 | ||
56 | print('Media ID: %s' % str(media_id)) | |
57 | ||
58 | ||
59 | def upload_append(self): | |
60 | ''' | |
61 | Uploads media in chunks and appends to chunks uploaded | |
62 | ''' | |
63 | segment_id = 0 | |
64 | bytes_sent = 0 | |
65 | file = open(self.video_filename, 'rb') | |
66 | ||
67 | while bytes_sent < self.total_bytes: | |
68 | chunk = file.read(4*1024*1024) | |
69 | ||
70 | print('APPEND') | |
71 | ||
72 | request_data = { | |
73 | 'command': 'APPEND', | |
74 | 'media_id': self.media_id, | |
75 | 'segment_index': segment_id | |
76 | } | |
77 | ||
78 | files = { | |
79 | 'media':chunk | |
80 | } | |
81 | ||
82 | req = requests.post(url=MEDIA_ENDPOINT_URL, data=request_data, files=files, auth=oauth) | |
83 | ||
84 | if req.status_code < 200 or req.status_code > 299: | |
85 | print(req.status_code) | |
86 | print(req.text) | |
87 | sys.exit(0) | |
88 | ||
89 | segment_id = segment_id + 1 | |
90 | bytes_sent = file.tell() | |
91 | ||
92 | print('%s of %s bytes uploaded' % (str(bytes_sent), str(self.total_bytes))) | |
93 | ||
94 | print('Upload chunks complete.') | |
95 | ||
96 | ||
97 | def upload_finalize(self): | |
98 | ''' | |
99 | Finalizes uploads and starts video processing | |
100 | ''' | |
101 | print('FINALIZE') | |
102 | ||
103 | request_data = { | |
104 | 'command': 'FINALIZE', | |
105 | 'media_id': self.media_id | |
106 | } | |
107 | ||
108 | req = requests.post(url=MEDIA_ENDPOINT_URL, data=request_data, auth=oauth) | |
109 | print(req.json()) | |
110 | ||
111 | self.processing_info = req.json().get('processing_info', None) | |
112 | self.check_status() | |
113 | ||
114 | ||
115 | def check_status(self): | |
116 | ''' | |
117 | Checks video processing status | |
118 | ''' | |
119 | if self.processing_info is None: | |
120 | return | |
121 | ||
122 | state = self.processing_info['state'] | |
123 | ||
124 | print('Media processing status is %s ' % state) | |
125 | ||
126 | if state == u'succeeded': | |
127 | return | |
128 | ||
129 | if state == u'failed': | |
130 | sys.exit(0) | |
131 | ||
132 | check_after_secs = self.processing_info['check_after_secs'] | |
133 | ||
134 | print('Checking after %s seconds' % str(check_after_secs)) | |
135 | time.sleep(check_after_secs) | |
136 | ||
137 | print('STATUS') | |
138 | ||
139 | request_params = { | |
140 | 'command': 'STATUS', | |
141 | 'media_id': self.media_id | |
142 | } | |
143 | ||
144 | req = requests.get(url=MEDIA_ENDPOINT_URL, params=request_params, auth=oauth) | |
145 | ||
146 | self.processing_info = req.json().get('processing_info', None) | |
147 | self.check_status() | |
148 | ||
149 | ||
150 | def tweet(self): | |
151 | ''' | |
152 | Publishes Tweet with attached video | |
153 | ''' | |
154 | request_data = { | |
d3ac7c48 | 155 | 'status': tweet_text, |
56d5cd36 | 156 | 'media_ids': self.media_id |
157 | } | |
158 | ||
159 | req = requests.post(url=POST_TWEET_URL, data=request_data, auth=oauth) | |
160 | print(req.json()) | |
161 | ||
162 | ||
163 | if __name__ == '__main__': | |
d3ac7c48 IK |
164 | if (len(sys.argv) < 2): |
165 | print('error: expected 2+ arguments, path to video (no spaces) and tweet text') | |
166 | sys.exit(1) | |
167 | VIDEO_FILENAME = sys.argv[1] | |
168 | global tweet_text | |
169 | tweet_text = ' '.join(sys.argv[2:]) | |
170 | ||
56d5cd36 | 171 | videoTweet = VideoTweet(VIDEO_FILENAME) |
172 | videoTweet.upload_init() | |
173 | videoTweet.upload_append() | |
174 | videoTweet.upload_finalize() | |
52ea40aa | 175 | videoTweet.tweet() |