Add media/upload endpoint.
authorFlorent Espanet <florent.esp@gmail.com>
Thu, 15 Jan 2015 10:49:41 +0000 (11:49 +0100)
committerFlorent Espanet <florent.esp@gmail.com>
Thu, 15 Jan 2015 10:49:41 +0000 (11:49 +0100)
The media/upload endpoint is located on a different server :
upload.twitter.com.

It returns a unique result model called `Media` containing the
`media_id` and the image infos.

See also :

* [doc](https://dev.twitter.com/rest/reference/post/media/upload)

tweepy/api.py
tweepy/binder.py
tweepy/models.py

index e822823f6c14c33f6b39103caf84602fa50ebba4..756839eac0a7d59cfc21cac54da98b75f969958c 100644 (file)
@@ -20,18 +20,21 @@ class API(object):
 
     def __init__(self, auth_handler=None,
                  host='api.twitter.com', search_host='search.twitter.com',
-                 cache=None, api_root='/1.1', search_root='',
-                 retry_count=0, retry_delay=0, retry_errors=None, timeout=60,
-                 parser=None, compression=False, wait_on_rate_limit=False,
+                 upload_host='upload.twitter.com', cache=None, api_root='/1.1',
+                 search_root='', upload_root='/1.1', retry_count=0,
+                 retry_delay=0, retry_errors=None, timeout=60, parser=None,
+                 compression=False, wait_on_rate_limit=False,
                  wait_on_rate_limit_notify=False, proxy=''):
         """ Api instance Constructor
 
         :param auth_handler:
         :param host:  url of the server of the rest api, default:'api.twitter.com'
         :param search_host: url of the search server, default:'search.twitter.com'
+        :param upload_host: url of the upload server, default:'upload.twitter.com'
         :param cache: Cache to query if a GET method is used, default:None
         :param api_root: suffix of the api version, default:'/1.1'
         :param search_root: suffix of the search version, default:''
+        :param upload_root: suffix of the upload version, default:'/1.1'
         :param retry_count: number of allowed retries, default:0
         :param retry_delay: delay in second between retries, default:0
         :param retry_errors: default:None
@@ -47,8 +50,10 @@ class API(object):
         self.auth = auth_handler
         self.host = host
         self.search_host = search_host
+        self.upload_host = upload_host
         self.api_root = api_root
         self.search_root = search_root
+        self.upload_root = upload_root
         self.cache = cache
         self.compression = compression
         self.retry_count = retry_count
@@ -184,6 +189,24 @@ class API(object):
             require_auth=True
         )
 
+    def media_upload(self, filename, *args, **kwargs):
+        """ :reference: https://dev.twitter.com/rest/reference/post/media/upload
+            :allowed_param:
+        """
+        f = kwargs.pop('file', None)
+        headers, post_data = API._pack_image(filename, 3072, form_field='media', f=f)
+        kwargs.update({'headers': headers, 'post_data': post_data})
+
+        return bind_api(
+            api=self,
+            path='/media/upload.json',
+            method='POST',
+            payload_type='media',
+            allowed_param=[],
+            require_auth=True,
+            upload_api=True
+        )(*args, **kwargs)
+
     def update_with_media(self, filename, *args, **kwargs):
         """ :reference: https://dev.twitter.com/rest/reference/post/statuses/update_with_media
             :allowed_param:'status', 'possibly_sensitive', 'in_reply_to_status_id', 'lat', 'long', 'place_id', 'display_coordinates'
index 12011a8df8bdc2af6106ced35f0d07ab1396e8f7..2ac614647d2d9bba28c187ce97a825cf6588416b 100644 (file)
@@ -33,6 +33,7 @@ def bind_api(**config):
         method = config.get('method', 'GET')
         require_auth = config.get('require_auth', False)
         search_api = config.get('search_api', False)
+        upload_api = config.get('upload_api', False)
         use_cache = config.get('use_cache', True)
         session = requests.Session()
 
@@ -61,6 +62,8 @@ def bind_api(**config):
             # Pick correct URL root to use
             if self.search_api:
                 self.api_root = api.search_root
+            elif self.upload_api:
+                self.api_root = api.upload_root
             else:
                 self.api_root = api.api_root
 
@@ -69,6 +72,8 @@ def bind_api(**config):
 
             if self.search_api:
                 self.host = api.search_host
+            elif self.upload_api:
+                self.host = api.upload_host
             else:
                 self.host = api.host
 
@@ -181,7 +186,6 @@ def bind_api(**config):
                                                 auth=auth,
                                                 proxies=self.api.proxy)
                 except Exception as e:
-
                     raise TweepError('Failed to send request: %s' % e)
                 rem_calls = resp.headers.get('x-rate-limit-remaining')
                 if rem_calls is not None:
index b8a3c15555e7a58ab1513565479e5f929321bde3..30456b257cd95c7553ffebbd5920fdb199c14d82 100644 (file)
@@ -458,6 +458,16 @@ class Place(Model):
         return results
 
 
+class Media(Model):
+
+    @classmethod
+    def parse(cls, api, json):
+        media = cls(api)
+        for k, v in json.items():
+            setattr(media, k, v)
+        return media
+
+
 class ModelFactory(object):
     """
     Used by parsers for creating instances
@@ -475,6 +485,7 @@ class ModelFactory(object):
     list = List
     relation = Relation
     relationship = Relationship
+    media = Media
 
     json = JSONModel
     ids = IDModel