From 68fa152fc200b545488d2c874b28659e473e6650 Mon Sep 17 00:00:00 2001 From: Shayne Sweeney Date: Wed, 23 Feb 2011 17:59:38 -0800 Subject: [PATCH 01/95] Init Image using object_from_dictionary for Python 2.5 support. --- instagram/models.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/instagram/models.py b/instagram/models.py index 2e6ddd13..ab39572f 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -9,7 +9,7 @@ def object_from_dictionary(cls, entry): return cls(**entry_str_dict) class Image(ApiModel): - + def __init__(self, url, width, height): self.url = url self.height = height @@ -21,23 +21,23 @@ def __init__(self, id=None, **kwargs): self.id = id for key,value in kwargs.iteritems(): setattr(self, key, value) - + def get_standard_resolution_url(self): return self.images['standard_resolution'].url - + @classmethod def object_from_dictionary(cls, entry): new_media = Media(id=entry['id']) - + new_media.user = User.object_from_dictionary(entry['user']) new_media.images = {} for version,version_info in entry['images'].iteritems(): - new_media.images[version] = Image(**version_info) + new_media.images[version] = Image.object_from_dictionary(version_info) if 'user_has_liked' in entry: new_media.user_has_liked = entry['user_has_liked'] new_media.like_count = entry['likes']['count'] - + new_media.comment_count = entry['comments']['count'] new_media.comments = [] for comment in entry['comments']['data']: @@ -57,7 +57,7 @@ def __init__(self, name, **kwargs): self.name = name for key,value in kwargs.iteritems(): setattr(self, key, value) - + def __str__(self): return "Tag %s" % self.name @@ -98,7 +98,7 @@ def object_from_dictionary(cls, entry): point, name=entry['name']) return location - + class User(ApiModel): def __init__(self, id, *args, **kwargs): @@ -114,5 +114,5 @@ class Relationship(ApiModel): def __init__(self, incoming_status="none", outgoing_status="none"): self.incoming_status = incoming_status self.outgoing_status = outgoing_status - + From 2ea1ed4ef54f5cadaa765f3a5c13aefe50fa3ef0 Mon Sep 17 00:00:00 2001 From: Mike Krieger Date: Thu, 17 Feb 2011 11:24:38 -0800 Subject: [PATCH 02/95] adding realtime support --- instagram/bind.py | 10 +++++++- instagram/client.py | 22 +++++++++++++++++ instagram/oauth2.py | 29 +++++++++++++++------- instagram/subscriptions.py | 49 ++++++++++++++++++++++++++++++++++++++ sample_app.py | 31 +++++++++++++++++++++--- tests.py | 3 --- 6 files changed, 128 insertions(+), 16 deletions(-) create mode 100644 instagram/subscriptions.py diff --git a/instagram/bind.py b/instagram/bind.py index 0e507e52..2888a6dc 100644 --- a/instagram/bind.py +++ b/instagram/bind.py @@ -36,6 +36,8 @@ class InstagramAPIMethod(object): paginates = config.get('paginates', False) root_class = config.get('root_class', None) response_type = config.get("response_type", "list") + include_secret = config.get("include_secret", False) + objectify_response = config.get("objectify_response", True) def __init__(self, api, *args, **kwargs): self.api = api @@ -87,6 +89,9 @@ def _do_api_request(self, url, method="GET", body=None, headers={}): response_objects = [] status_code = content_obj['meta']['code'] if status_code == 200: + if not self.objectify_response: + return content_obj, None + if self.response_type == 'list': for entry in content_obj['data']: obj = self.root_class.object_from_dictionary(entry) @@ -106,7 +111,10 @@ def _paginator_with_url(self, url, method="GET", body=None, headers={}): return def execute(self): - url, method, body, headers = OAuth2Request(self.api).prepare_request(self.method, self.path, self.parameters) + url, method, body, headers = OAuth2Request(self.api).prepare_request(self.method, + self.path, + self.parameters, + include_secret = self.include_secret) if self.as_generator: return self._paginator_with_url(url, method, body, headers) else: diff --git a/instagram/client.py b/instagram/client.py index e5cc90dd..2f4148bb 100644 --- a/instagram/client.py +++ b/instagram/client.py @@ -181,3 +181,25 @@ def _inner(self, *args, **kwargs): unblock_user = _make_relationship_shortcut('unblock') approve_user_request = _make_relationship_shortcut('approve') ignore_user_request = _make_relationship_shortcut('ignore') + + + + def _make_subscription_action(method): + return bind_method( + path = "/subscriptions", + method = method, + accepts_parameters = ["object", + "aspect", + "object_id", # Optional if subscribing to all users + "callback_url", + "lat", # Geography + "lng", # Geography + "radius", # Geography + "verify_token"], + include_secret = True, + objectify_response = False + ) + + create_subscription = _make_subscription_action('POST') + list_subscriptions = _make_subscription_action('GET') + delete_subscriptions = _make_subscription_action('DELETE') \ No newline at end of file diff --git a/instagram/oauth2.py b/instagram/oauth2.py index 12b67ea7..1df10701 100644 --- a/instagram/oauth2.py +++ b/instagram/oauth2.py @@ -117,21 +117,28 @@ def get_request(self, path, **kwargs): def post_request(self, path, **kwargs): return self.make_request(self.prepare_request("POST", path, kwargs)) - def _full_url(self, path): - return "%s://%s%s%s%s" % (self.api.protocol, self.api.host, self.api.base_path, path, self._auth_query()) + def _full_url(self, path, include_secret=False): + return "%s://%s%s%s%s" % (self.api.protocol, + self.api.host, + self.api.base_path, + path, + self._auth_query(include_secret)) - def _full_url_with_params(self, path, params): - return (self._full_url(path) + self._full_query_with_params(params)) + def _full_url_with_params(self, path, params, include_secret=False): + return (self._full_url(path, include_secret) + self._full_query_with_params(params)) def _full_query_with_params(self, params): params = ("&" + urllib.urlencode(params)) if params else "" return params - def _auth_query(self): + def _auth_query(self, include_secret=False): if self.api.access_token: return ("?%s=%s" % (self.api.access_token_field, self.api.access_token)) elif self.api.client_id: - return ("?client_id=%s" % (self.api.client_id)) + base = ("?client_id=%s" % (self.api.client_id)) + if include_secret: + base += "&client_secret=%s" % (self.api.client_secret) + return base def _post_body(self, params): return urllib.urlencode(params) @@ -167,7 +174,11 @@ def encode_file(field_name): return body, headers - def prepare_request(self, method, path, params): + def prepare_and_make_request(self, method, path, params, include_secret=False): + url, method, body, headers = self.prepare_request(method, path, params, include_secret) + return self.make_request(url, method, body, headers) + + def prepare_request(self, method, path, params, include_secret=False): url = body = None headers = {} @@ -175,9 +186,9 @@ def prepare_request(self, method, path, params): if method == "POST": body = self._post_body(params) headers = {'Content-type': 'application/x-www-form-urlencoded'} - url = self._full_url(path) + url = self._full_url(path, include_secret) else: - url = self._full_url_with_params(path, params) + url = self._full_url_with_params(path, params, include_secret) else: body, headers = encode_multipart(params, params['files']) url = self._full_url(path) diff --git a/instagram/subscriptions.py b/instagram/subscriptions.py new file mode 100644 index 00000000..24cf6676 --- /dev/null +++ b/instagram/subscriptions.py @@ -0,0 +1,49 @@ +import hmac +import hashlib +import simplejson + +class SubscriptionType: + TAG = 'tag' + USER = 'user' + GEOGRAPHY = 'geography' + LOCATION = 'location' + +class SubscriptionVerifyError(Exception): + pass + +class SubscriptionsReactor(object): + + callbacks = {} + + def _process_update(self, update): + object_callbacks = self.callbacks.get(update['object'], []) + + for callback in object_callbacks: + callback(update) + + def process(self, client_secret, raw_response, x_hub_signature): + if not self._verify_signature(client_secret, raw_response, x_hub_signature): + raise SubscriptionVerifyError("X-Hub-Signature and hmac digest did not match") + + response = simplejson.loads(raw_response) + for update in response: + self._process_update(update) + + def register_callback(self, object_type, callback): + cb_list = self.callbacks.get(object_type, []) + + if callback not in cb_list: + cb_list.append(callback) + self.callbacks[object_type] = cb_list + + def deregister_callback(self, object_type, callback): + callbacks = self.callbacks.get(object_type, []) + callbacks.remove(callback) + + def _verify_signature(self, client_secret, raw_response, x_hub_signature): + digest = hmac.new(client_secret.encode('utf-8'), + msg=raw_response.encode('utf-8'), + digestmod=hashlib.sha1 + ).hexdigest() + return digest == x_hub_signature + diff --git a/sample_app.py b/sample_app.py index e93cb2d3..ee746d62 100644 --- a/sample_app.py +++ b/sample_app.py @@ -1,5 +1,8 @@ -from bottle import route, run, request -from instagram import client +import bottle +from bottle import route, post, run, request +from instagram import client, subscriptions + +bottle.debug(True) CONFIG = { 'client_id': '', @@ -9,6 +12,12 @@ unauthenticated_api = client.InstagramAPI(**CONFIG) +def process_tag_update(update): + print update + +reactor = subscriptions.SubscriptionsReactor() +reactor.register_callback(subscriptions.SubscriptionType.TAG, process_tag_update) + @route('/') def home(): try: @@ -36,4 +45,20 @@ def on_callback(): except Exception, e: print e -run(host='localhost', port=8515) +@route('/realtime_callback') +@post('/realtime_callback') +def on_realtime_callback(): + mode = request.GET.get("hub.mode") + challenge = request.GET.get("hub.challenge") + verify_token = request.GET.get("hub.verify_token") + if challenge: + return challenge + else: + x_hub_signature = request.header.get('X-Hub-Signature') + raw_response = request.body.read() + try: + reactor.process(CONFIG['client_secret'], raw_response, x_hub_signature) + except subscriptions.SubscriptionVerifyError: + print "Signature mismatch" + +run(host='localhost', port=8515, reloader=True) \ No newline at end of file diff --git a/tests.py b/tests.py index c2b2bbac..6df2b6d1 100755 --- a/tests.py +++ b/tests.py @@ -48,7 +48,6 @@ def request(self, url, method="GET", body=None, headers={}): active_call = None class TestInstagramAPI(client.InstagramAPI): - def __getattribute__(self, attr): global active_call actual_val = super(TestInstagramAPI, self).__getattribute__(attr) @@ -178,10 +177,8 @@ def test_change_relationship(self): self.api.follow_user(user_id='10') self.api.unfollow_user(user_id='10') - if __name__ == '__main__': if not TEST_AUTH: del InstagramAuthTests unittest.main() - From 83b9d15c489d185c10b90e03a450fceaf00a6c54 Mon Sep 17 00:00:00 2001 From: Mike Krieger Date: Thu, 24 Feb 2011 11:08:16 -0800 Subject: [PATCH 03/95] updating accepted parameters for subscriptions --- instagram/client.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/instagram/client.py b/instagram/client.py index 2f4148bb..221dfafb 100644 --- a/instagram/client.py +++ b/instagram/client.py @@ -184,22 +184,28 @@ def _inner(self, *args, **kwargs): - def _make_subscription_action(method): + def _make_subscription_action(method, include=None, exclude=None): + accepts_parameters = ["object", + "aspect", + "object_id", # Optional if subscribing to all users + "callback_url", + "lat", # Geography + "lng", # Geography + "radius", # Geography + "verify_token"] + + if include: + accepts_parameters.extend(include) + if exclude: + accepts_parameters = [x for x in accepts_parameters if x not in exclude] return bind_method( path = "/subscriptions", method = method, - accepts_parameters = ["object", - "aspect", - "object_id", # Optional if subscribing to all users - "callback_url", - "lat", # Geography - "lng", # Geography - "radius", # Geography - "verify_token"], + accepts_parameters = accepts_parameters, include_secret = True, objectify_response = False ) create_subscription = _make_subscription_action('POST') list_subscriptions = _make_subscription_action('GET') - delete_subscriptions = _make_subscription_action('DELETE') \ No newline at end of file + delete_subscriptions = _make_subscription_action('DELETE', exclude=['object_id'], include=['id']) \ No newline at end of file From 2d4199113afe4c3b1103aaef4a9b3e085972a27e Mon Sep 17 00:00:00 2001 From: Mike Krieger Date: Thu, 24 Feb 2011 11:09:40 -0800 Subject: [PATCH 04/95] Bumping version number --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index ac856706..2aaf1c0b 100644 --- a/setup.py +++ b/setup.py @@ -2,12 +2,12 @@ from setuptools import setup, find_packages setup(name="python-instagram", - version="0.5.0", + version="0.6.0", description="Instagram API client", license="MIT", install_requires=["simplejson","httplib2"], author="Instagram, Inc", - author_email="developers@instagram.com", + author_email="apidevelopers@instagram.com", url="http://github.com/Instagram/python-instagram", packages = find_packages(), keywords= "instagram", From 1da5ad488b258ad4324e1d5b49c994c517e602c4 Mon Sep 17 00:00:00 2001 From: Shayne Sweeney Date: Mon, 28 Feb 2011 01:11:08 -0800 Subject: [PATCH 05/95] Added method exchange_user_id_for_access_token which gives you an access_token response for a client who is already authorized. --- instagram/oauth2.py | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/instagram/oauth2.py b/instagram/oauth2.py index 1df10701..66db8e65 100644 --- a/instagram/oauth2.py +++ b/instagram/oauth2.py @@ -22,7 +22,7 @@ class OAuth2API(object): protocol = "https" # override with 'Instagram', etc api_name = "Generic API" - + def __init__(self, client_id=None, client_secret=None, access_token=None, redirect_uri=None): self.client_id = client_id self.client_secret = client_secret @@ -32,7 +32,7 @@ def __init__(self, client_id=None, client_secret=None, access_token=None, redire def get_authorize_url(self, scope=None): req = OAuth2AuthExchangeRequest(self) return req.get_authorize_url(scope = scope) - + def get_authorize_login_url(self, scope=None): """ scope should be a tuple or list of requested scope access levels """ req = OAuth2AuthExchangeRequest(self) @@ -42,6 +42,10 @@ def exchange_code_for_access_token(self, code): req = OAuth2AuthExchangeRequest(self) return req.exchange_for_access_token(code = code) + def exchange_user_id_for_access_token(self, user_id): + req = OAuth2AuthExchangeRequest(self) + return req.exchange_for_access_token(user_id = user_id) + def exchange_xauth_login_for_access_token(self, username, password, scope=None): """ scope should be a tuple or list of requested scope access levels """ req = OAuth2AuthExchangeRequest(self) @@ -63,7 +67,7 @@ def _url_for_authorize(self, scope=None): url_params = urllib.urlencode(client_params) return "%s?%s" % (self.api.authorize_url, url_params) - def _data_for_exchange(self, code=None, username=None, password=None, scope=None): + def _data_for_exchange(self, code=None, username=None, password=None, scope=None, user_id=None): client_params = { "client_id": self.api.client_id, "client_secret": self.api.client_secret, @@ -73,11 +77,13 @@ def _data_for_exchange(self, code=None, username=None, password=None, scope=None if code: client_params.update(code=code) elif username and password: - client_params.update(username = username, + client_params.update(username = username, password = password, grant_type = "password") if scope: client_params.update(scope = ' '.join(scope)) + elif user_id: + client_params.update(user_id = user_id) return urllib.urlencode(client_params) def get_authorize_url(self, scope=None): @@ -93,8 +99,8 @@ def get_authorize_login_url(self, scope=None): redirected_to = response['content-location'] return redirected_to - def exchange_for_access_token(self, code=None, username=None, password=None, scope=None): - data = self._data_for_exchange(code, username, password, scope = scope) + def exchange_for_access_token(self, code=None, username=None, password=None, scope=None, user_id=None): + data = self._data_for_exchange(code, username, password, scope = scope, user_id = user_id) http_object = Http() url = self.api.access_token_url response, content = http_object.request(url, method="POST", body=data) @@ -113,10 +119,10 @@ def url_for_get(self, path, parameters): def get_request(self, path, **kwargs): return self.make_request(self.prepare_request("GET", path, kwargs)) - + def post_request(self, path, **kwargs): return self.make_request(self.prepare_request("POST", path, kwargs)) - + def _full_url(self, path, include_secret=False): return "%s://%s%s%s%s" % (self.api.protocol, self.api.host, @@ -142,7 +148,7 @@ def _auth_query(self, include_secret=False): def _post_body(self, params): return urllib.urlencode(params) - + def _encode_multipart(params, files): boundary = "MuL7Ip4rt80uND4rYF0o" @@ -153,7 +159,7 @@ def encode_field(field_name): return ("--" + boundary, 'Content-Disposition: form-data; name="%s"' % (field_name), "", str(params[field_name])) - + def encode_file(field_name): file_name, file_handle = files[field_name] return ("--" + boundary, @@ -168,10 +174,10 @@ def encode_file(field_name): lines.extend(encode_file(field)) lines.extend(("--%s--" % (boundary), "")) body = "\r\n".join (lines) - + headers = {"Content-Type": "multipart/form-data; boundary=" + boundary, "Content-Length": str(len(body))} - + return body, headers def prepare_and_make_request(self, method, path, params, include_secret=False): @@ -185,7 +191,7 @@ def prepare_request(self, method, path, params, include_secret=False): if not params.get('files'): if method == "POST": body = self._post_body(params) - headers = {'Content-type': 'application/x-www-form-urlencoded'} + headers = {'Content-type': 'application/x-www-form-urlencoded'} url = self._full_url(path, include_secret) else: url = self._full_url_with_params(path, params, include_secret) From 5329ddf6e6db6214e4c481ec94d046d2915a1ec7 Mon Sep 17 00:00:00 2001 From: Mike Krieger Date: Wed, 1 Jun 2011 20:03:37 -0700 Subject: [PATCH 06/95] Adding user_liked_media endpoint --- fixtures/user_incoming_requests.json | 15 ++++++ fixtures/user_liked_media.json | 79 ++++++++++++++++++++++++++++ instagram/client.py | 6 +++ tests.py | 3 ++ 4 files changed, 103 insertions(+) create mode 100644 fixtures/user_incoming_requests.json create mode 100644 fixtures/user_liked_media.json diff --git a/fixtures/user_incoming_requests.json b/fixtures/user_incoming_requests.json new file mode 100644 index 00000000..340b2389 --- /dev/null +++ b/fixtures/user_incoming_requests.json @@ -0,0 +1,15 @@ +{ + "pagination": { + }, + "meta": { + "code": 200 + }, + "data": [ + { + "username": "kevin", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_3_75sq_1295493319_debug.jpg", + "id": "3" + } + ] +} + diff --git a/fixtures/user_liked_media.json b/fixtures/user_liked_media.json new file mode 100644 index 00000000..db23d274 --- /dev/null +++ b/fixtures/user_liked_media.json @@ -0,0 +1,79 @@ +{ + "pagination": { + "next_url": "http://localhost:8000/publicapi/v1/users/self/feed?q=iphone&lat=37.771&access_token=DEBUG&lng=-122.221&max_id=3382520", + "next_max_like_id": 50 + }, + "meta": { + "code": 200 + }, + "data": [ + { + "type": "image", + "location": { + "latitude": 37.781089999999999, + "id": null, + "longitude": -122.3946, + "name": null + }, + "comments": { + "count": 1, + "data": [ + { + "created_time": "1296272101", + "text": "O hai.", + "from": { + "username": "mikeyk", + "first_name": "Mike", + "last_name": "Krieger!!", + "type": "user", + "id": "4" + }, + "id": "2611719" + } + ] + }, + "caption": { + "created_time": "1296272101", + "text": "O hai.", + "from": { + "username": "mikeyk", + "first_name": "Mike", + "last_name": "Krieger!!", + "type": "user", + "id": "4" + }, + "id": "2611719" + }, + "link": "http://localhost:8000/p/M5z4/", + "likes": { + "count": 0 + }, + "created_time": "1296673135", + "images": { + "low_resolution": { + "url": "http://distillery-dev.s3.amazonaws.com/media/2011/02/02/b6073350d89644ccb253b89ead6c75da_6.jpg", + "width": 480, + "height": 480 + }, + "thumbnail": { + "url": "http://distillery-dev.s3.amazonaws.com/media/2011/02/02/b6073350d89644ccb253b89ead6c75da_5.jpg", + "width": 150, + "height": 150 + }, + "standard_resolution": { + "url": "http://distillery-dev.s3.amazonaws.com/media/2011/02/02/b6073350d89644ccb253b89ead6c75da_7.jpg", + "width": 612, + "height": 612 + } + }, + "user_has_liked": false, + "id": "3382520", + "user": { + "username": "mikeyk", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_4_75sq_1292324747_debug.jpg", + "id": "4" + } + } + ] +} + diff --git a/instagram/client.py b/instagram/client.py index 221dfafb..53d5fc5b 100644 --- a/instagram/client.py +++ b/instagram/client.py @@ -85,6 +85,12 @@ def __init__(self, *args, **kwargs): root_class = Media, paginates = True) + user_liked_media = bind_method( + path = "/users/self/media/liked", + accepts_parameters = MEDIA_ACCEPT_PARAMETERS, + root_class = Media, + paginates = True) + user_recent_media = bind_method( path = "/users/{user_id}/media/recent", accepts_parameters = MEDIA_ACCEPT_PARAMETERS + ['user_id'], diff --git a/tests.py b/tests.py index 6df2b6d1..d645a530 100755 --- a/tests.py +++ b/tests.py @@ -113,6 +113,9 @@ def test_generator_user_feed(self): for page in generator: str(generator) + def test_user_liked_media(self): + self.api.user_liked_media(count=10) + def test_user_recent_media(self): self.api.user_recent_media(count=10) From 73bcdf131d92c0441c989195c48485cab0ba4bdc Mon Sep 17 00:00:00 2001 From: Mike Krieger Date: Wed, 1 Jun 2011 20:33:22 -0700 Subject: [PATCH 07/95] handle non-ID'd locations --- instagram/models.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/instagram/models.py b/instagram/models.py index ab39572f..5a3fdf84 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -92,11 +92,11 @@ def __init__(self, id, *args, **kwargs): def object_from_dictionary(cls, entry): point = None if entry['latitude']: - point = Point(entry['latitude'], - entry['longitude']) - location = cls(entry['id'], + point = Point(entry.get('latitude'), + entry.get('longitude')) + location = cls(entry.get('id'), point, - name=entry['name']) + name=entry.get('name')) return location class User(ApiModel): From 2ce61e5dd6c32620bd09eac5baf908b0c14c780c Mon Sep 17 00:00:00 2001 From: Mike Krieger Date: Sun, 19 Jun 2011 22:46:19 -0700 Subject: [PATCH 08/95] Fixing user follows / followed-by endpoints --- instagram/client.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/instagram/client.py b/instagram/client.py index 53d5fc5b..c2d80195 100644 --- a/instagram/client.py +++ b/instagram/client.py @@ -103,13 +103,15 @@ def __init__(self, *args, **kwargs): root_class = User) user_follows = bind_method( - path = "/users/{user_id}/follows/users", + path = "/users/{user_id}/follows", accepts_parameters = ["user_id"], + paginates = True, root_class = User) user_followed_by = bind_method( - path = "/users/{user_id}/followed-by/users", + path = "/users/{user_id}/followed-by", accepts_parameters = ["user_id"], + paginates = True, root_class = User) user = bind_method( @@ -153,16 +155,6 @@ def __init__(self, *args, **kwargs): root_class = Tag, response_type = "entry") - user_follows = bind_method( - path = "/users/self/follows", - root_class = User, - paginates = True) - - user_followed_by = bind_method( - path = "/users/self/followed-by", - root_class = User, - paginates = True) - user_incoming_requests = bind_method( path = "/users/self/requested-by", root_class = User) From 0745e51eb18788015269197b3772b7172e26c253 Mon Sep 17 00:00:00 2001 From: William Bert Date: Mon, 5 Sep 2011 09:56:40 -0400 Subject: [PATCH 09/95] Specified basic scope explicitly when authenticating because API gives error if scope is blank. --- get_access_token.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/get_access_token.py b/get_access_token.py index 7e32a07d..f8977a20 100644 --- a/get_access_token.py +++ b/get_access_token.py @@ -19,6 +19,9 @@ redirect_uri = raw_input("Redirect URI: ").strip() raw_scope = raw_input("Requested scope (separated by spaces, blank for just basic read): ").strip() scope = raw_scope.split(' ') +# For basic, API seems to need to be set explicitly +if not scope or scope == [""]: + scope = ["basic"] api = InstagramAPI(client_id=client_id, client_secret=client_secret, redirect_uri=redirect_uri) redirect_uri = api.get_authorize_login_url(scope = scope) From caa3bba66dc90eed90542cac7c8e33ae1b1c33cb Mon Sep 17 00:00:00 2001 From: William Bert Date: Mon, 5 Sep 2011 10:27:39 -0400 Subject: [PATCH 10/95] Updated README.md to say media_popular and standard_resolution. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 270792a0..1bed3650 100644 --- a/README.md +++ b/README.md @@ -28,9 +28,9 @@ Usage access_token = "..." api = InstagramAPI(access_token=access_token) - popular_media = api.popular_media(count=20) + popular_media = api.media_popular(count=20) for media in popular_media: - print media.images['high_resolution'].url + print media.images['standard_resolution'].url Sample app ------ From 53daf35a6124fac057458f436322938dbaacab41 Mon Sep 17 00:00:00 2001 From: William Bert Date: Mon, 5 Sep 2011 10:48:54 -0400 Subject: [PATCH 11/95] Updated sample_app docs to specify that oauth_callback must be part of the call back uri. --- README.md | 4 ++-- sample_app.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1bed3650..6a7bf07d 100644 --- a/README.md +++ b/README.md @@ -36,8 +36,8 @@ Sample app ------ We also provide a one-file sample app using bottle (you'll have to 'pip install bottle' first). To try it out: - * Set your redirect URI to 'http://localhost:8515' in your dev profile - * Open up sample\_app.py, update it with your client\_id and secret + * Set your redirect URI to 'http://localhost:8515/outh_callback' in your dev profile + * Open up sample\_app.py, update it with your client\_id and secret, and set redirect URI to 'http://localhost:8515/outh_callback' * Run the file; it will host a local server on port 8515. * Try visiting http://localhost:8515 in your browser diff --git a/sample_app.py b/sample_app.py index ee746d62..81fe74ef 100644 --- a/sample_app.py +++ b/sample_app.py @@ -7,7 +7,7 @@ CONFIG = { 'client_id': '', 'client_secret': '', - 'redirect_uri': '' + 'redirect_uri': 'http://localhost:8515/oauth_callback' } unauthenticated_api = client.InstagramAPI(**CONFIG) From 63d826d90f112b43e1a7566cb54c3b1242f15357 Mon Sep 17 00:00:00 2001 From: Henry Cooke Date: Sun, 16 Oct 2011 16:17:52 +0100 Subject: [PATCH 12/95] added Caption field to Media model objects --- instagram/models.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/instagram/models.py b/instagram/models.py index 5a3fdf84..a707c424 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -47,7 +47,11 @@ def object_from_dictionary(cls, entry): if entry['location']: new_media.location = Location.object_from_dictionary(entry['location']) - + + new_media.caption = None + if entry['caption']: + new_media.caption = Comment.object_from_dictionary( entry['caption'] ) + new_media.link = entry['link'] return new_media From 55c0950a7b1aeb6a26952301b72a879243abb723 Mon Sep 17 00:00:00 2001 From: Shreyans Bhansali Date: Wed, 15 Feb 2012 23:15:34 -0500 Subject: [PATCH 13/95] return raw json responses instead of media objects --- instagram/bind.py | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/instagram/bind.py b/instagram/bind.py index 2888a6dc..100eefab 100644 --- a/instagram/bind.py +++ b/instagram/bind.py @@ -42,6 +42,7 @@ class InstagramAPIMethod(object): def __init__(self, api, *args, **kwargs): self.api = api self.as_generator = kwargs.pop("as_generator", False) + self.return_raw_response = kwargs.pop("return_raw_responses", False) self.max_pages = kwargs.pop("max_pages", 3) self.parameters = {} self._build_parameters(args, kwargs) @@ -87,6 +88,7 @@ def _do_api_request(self, url, method="GET", body=None, headers={}): raise InstagramAPIError(response['status'], "Rate limited", "Your client is making too many request per second") content_obj = simplejson.loads(content) response_objects = [] + raw_response = [] status_code = content_obj['meta']['code'] if status_code == 200: if not self.objectify_response: @@ -94,20 +96,30 @@ def _do_api_request(self, url, method="GET", body=None, headers={}): if self.response_type == 'list': for entry in content_obj['data']: - obj = self.root_class.object_from_dictionary(entry) - response_objects.append(obj) + if self.return_raw_response: + raw_response.append(entry) + else: + obj = self.root_class.object_from_dictionary(entry) + response_objects.append(obj) elif self.response_type == 'entry': - response_objects = self.root_class.object_from_dictionary(content_obj['data']) - return response_objects, content_obj.get('pagination', {}).get('next_url') + data = content_obj['data'] + if self.return_raw_response: + raw_response = data + else: + response_objects = self.root_class.object_from_dictionary(data) + if self.return_raw_response: + return raw_response, content_obj.get('pagination', {}).get('next_url') + else: + return response_objects, content_obj.get('pagination', {}).get('next_url') else: raise InstagramAPIError(status_code, content_obj['meta']['error_type'], content_obj['meta']['error_message']) def _paginator_with_url(self, url, method="GET", body=None, headers={}): pages_read = 0 while url and pages_read < self.max_pages: - response_objects, url = self._do_api_request(url, method, body, headers) - pages_read += 1 - yield response_objects, url + resp, url = self._do_api_request(url, method, body, headers) + pages_read += 1 + yield resp, url return def execute(self): From faaad4cd47d709004fe2340a23cd6b38876725ee Mon Sep 17 00:00:00 2001 From: Shreyans Bhansali Date: Wed, 15 Feb 2012 23:20:21 -0500 Subject: [PATCH 14/95] return_json instead of media obeject --- instagram/bind.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/instagram/bind.py b/instagram/bind.py index 100eefab..0cd2349f 100644 --- a/instagram/bind.py +++ b/instagram/bind.py @@ -42,7 +42,7 @@ class InstagramAPIMethod(object): def __init__(self, api, *args, **kwargs): self.api = api self.as_generator = kwargs.pop("as_generator", False) - self.return_raw_response = kwargs.pop("return_raw_responses", False) + self.return_json = kwargs.pop("return_json", False) self.max_pages = kwargs.pop("max_pages", 3) self.parameters = {} self._build_parameters(args, kwargs) @@ -87,8 +87,7 @@ def _do_api_request(self, url, method="GET", body=None, headers={}): if response['status'] == '503': raise InstagramAPIError(response['status'], "Rate limited", "Your client is making too many request per second") content_obj = simplejson.loads(content) - response_objects = [] - raw_response = [] + responses = [] status_code = content_obj['meta']['code'] if status_code == 200: if not self.objectify_response: @@ -96,21 +95,18 @@ def _do_api_request(self, url, method="GET", body=None, headers={}): if self.response_type == 'list': for entry in content_obj['data']: - if self.return_raw_response: - raw_response.append(entry) + if self.return_json: + responses.append(entry) else: obj = self.root_class.object_from_dictionary(entry) - response_objects.append(obj) + responses.append(obj) elif self.response_type == 'entry': data = content_obj['data'] - if self.return_raw_response: - raw_response = data + if self.return_json: + responses = data else: - response_objects = self.root_class.object_from_dictionary(data) - if self.return_raw_response: - return raw_response, content_obj.get('pagination', {}).get('next_url') - else: - return response_objects, content_obj.get('pagination', {}).get('next_url') + responses = self.root_class.object_from_dictionary(data) + return responses, content_obj.get('pagination', {}).get('next_url') else: raise InstagramAPIError(status_code, content_obj['meta']['error_type'], content_obj['meta']['error_message']) From 9e63cf57e974904912557e4edb21b289f61e35f9 Mon Sep 17 00:00:00 2001 From: Shreyans Bhansali Date: Wed, 15 Feb 2012 23:23:16 -0500 Subject: [PATCH 15/95] return_json api_responses, not media objects --- instagram/bind.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/instagram/bind.py b/instagram/bind.py index 0cd2349f..b05b190b 100644 --- a/instagram/bind.py +++ b/instagram/bind.py @@ -87,7 +87,7 @@ def _do_api_request(self, url, method="GET", body=None, headers={}): if response['status'] == '503': raise InstagramAPIError(response['status'], "Rate limited", "Your client is making too many request per second") content_obj = simplejson.loads(content) - responses = [] + api_responses = [] status_code = content_obj['meta']['code'] if status_code == 200: if not self.objectify_response: @@ -96,26 +96,26 @@ def _do_api_request(self, url, method="GET", body=None, headers={}): if self.response_type == 'list': for entry in content_obj['data']: if self.return_json: - responses.append(entry) + api_responses.append(entry) else: obj = self.root_class.object_from_dictionary(entry) - responses.append(obj) + api_responses.append(obj) elif self.response_type == 'entry': data = content_obj['data'] if self.return_json: - responses = data + api_responses = data else: - responses = self.root_class.object_from_dictionary(data) - return responses, content_obj.get('pagination', {}).get('next_url') + api_responses = self.root_class.object_from_dictionary(data) + return api_responses, content_obj.get('pagination', {}).get('next_url') else: raise InstagramAPIError(status_code, content_obj['meta']['error_type'], content_obj['meta']['error_message']) def _paginator_with_url(self, url, method="GET", body=None, headers={}): pages_read = 0 while url and pages_read < self.max_pages: - resp, url = self._do_api_request(url, method, body, headers) + api_responses, url = self._do_api_request(url, method, body, headers) pages_read += 1 - yield resp, url + yield api_responses, url return def execute(self): From fef1e63eb6dffa543f0b30c424a6476defdacd38 Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Thu, 16 Feb 2012 18:21:46 +0000 Subject: [PATCH 16/95] Disable SSL validation. The Instagram API is signed with a GeoTrust Global CA. Unfortunately this CA root certificate is not part of the default cacert.txt file that ships with httplib2. Rather than ship the certificate file, let's just ignore the SSL validation issue. Once the upstream bug is fixed, http://code.google.com/p/httplib2/issues/detail?id=156 we can revert this change. --- instagram/oauth2.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/instagram/oauth2.py b/instagram/oauth2.py index 66db8e65..12654002 100644 --- a/instagram/oauth2.py +++ b/instagram/oauth2.py @@ -90,7 +90,7 @@ def get_authorize_url(self, scope=None): return self._url_for_authorize(scope = scope) def get_authorize_login_url(self, scope=None): - http_object = Http() + http_object = Http(disable_ssl_certificate_validation=True) url = self._url_for_authorize(scope = scope) response, content = http_object.request(url) @@ -101,7 +101,7 @@ def get_authorize_login_url(self, scope=None): def exchange_for_access_token(self, code=None, username=None, password=None, scope=None, user_id=None): data = self._data_for_exchange(code, username, password, scope = scope, user_id = user_id) - http_object = Http() + http_object = Http(disable_ssl_certificate_validation=True) url = self.api.access_token_url response, content = http_object.request(url, method="POST", body=data) parsed_content = simplejson.loads(content) @@ -204,5 +204,5 @@ def prepare_request(self, method, path, params, include_secret=False): def make_request(self, url, method="GET", body=None, headers={}): if not 'User-Agent' in headers: headers.update({"User-Agent":"%s Python Client" % self.api.api_name}) - http_obj = Http() + http_obj = Http(disable_ssl_certificate_validation=True) return http_obj.request(url, method, body=body, headers=headers) From c80ef51a9a78d944bb75fd59829f7d765d4ab326 Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Thu, 16 Feb 2012 17:48:49 +0000 Subject: [PATCH 17/95] Not all Locations have latitude / longitude. Check for that before constructing the Point Without this small fix, repeatedly doing a query will eventually result in: >>> from instagram.client import InstagramAPI >>> instagram_api = InstagramAPI(**{'client_id': 'something', 'client_secret': 'hidden'}) >>> instagram_api.media_popular() Traceback (most recent call last): File "", line 1, in File "instagram/bind.py", line 130, in _call return method.execute() File "instagram/bind.py", line 121, in execute content, next = self._do_api_request(url, method, body, headers) File "instagram/bind.py", line 97, in _do_api_request obj = self.root_class.object_from_dictionary(entry) File "instagram/models.py", line 49, in object_from_dictionary new_media.location = Location.object_from_dictionary(entry['location']) File "instagram/models.py", line 95, in object_from_dictionary if entry['latitude']: KeyError: 'latitude' >>> --- instagram/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instagram/models.py b/instagram/models.py index a707c424..8b70c9e9 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -95,7 +95,7 @@ def __init__(self, id, *args, **kwargs): @classmethod def object_from_dictionary(cls, entry): point = None - if entry['latitude']: + if 'latitude' in entry: point = Point(entry.get('latitude'), entry.get('longitude')) location = cls(entry.get('id'), From bd9fd8a9c2870776bfb91bc95a7d77e080f1992e Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Mon, 20 Feb 2012 16:24:19 +0000 Subject: [PATCH 18/95] As well as returning the access_token, return who it is associated to Returning the access_token is useful, but even more important is knowing which user has granted us access. --- instagram/oauth2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instagram/oauth2.py b/instagram/oauth2.py index 66db8e65..cfd592b2 100644 --- a/instagram/oauth2.py +++ b/instagram/oauth2.py @@ -107,7 +107,7 @@ def exchange_for_access_token(self, code=None, username=None, password=None, sco parsed_content = simplejson.loads(content) if int(response['status']) != 200: raise OAuth2AuthExchangeError(parsed_content.get("message", "")) - return parsed_content['access_token'] + return parsed_content['access_token'], parsed_content['user'] class OAuth2Request(object): From 4867b598a6fa0dc54b7403b0d1ccf77d60237808 Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Mon, 20 Feb 2012 16:25:34 +0000 Subject: [PATCH 19/95] Relationship changes are a POSTed. The method was not specified, so it defaults to GET. And, whilst the GET seems to work, it actually does not. --- instagram/client.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/instagram/client.py b/instagram/client.py index c2d80195..8e664c4c 100644 --- a/instagram/client.py +++ b/instagram/client.py @@ -160,6 +160,7 @@ def __init__(self, *args, **kwargs): root_class = User) change_user_relationship = bind_method( + method = "POST", path = "/users/{user_id}/relationship", root_class = Relationship, accepts_parameters = ["user_id", "action"], @@ -206,4 +207,4 @@ def _make_subscription_action(method, include=None, exclude=None): create_subscription = _make_subscription_action('POST') list_subscriptions = _make_subscription_action('GET') - delete_subscriptions = _make_subscription_action('DELETE', exclude=['object_id'], include=['id']) \ No newline at end of file + delete_subscriptions = _make_subscription_action('DELETE', exclude=['object_id'], include=['id']) From 94c323097eab2cb6e4fbb742e6972547f1d14ec6 Mon Sep 17 00:00:00 2001 From: Mike Krieger Date: Thu, 23 Feb 2012 17:16:50 -0800 Subject: [PATCH 20/95] Minor spacing fix --- instagram/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/instagram/models.py b/instagram/models.py index 8b70c9e9..c709e699 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -47,11 +47,11 @@ def object_from_dictionary(cls, entry): if entry['location']: new_media.location = Location.object_from_dictionary(entry['location']) - + new_media.caption = None if entry['caption']: - new_media.caption = Comment.object_from_dictionary( entry['caption'] ) - + new_media.caption = Comment.object_from_dictionary(entry['caption']) + new_media.link = entry['link'] return new_media From c95107cf03e07320792e4d181f3e687d9e6caed1 Mon Sep 17 00:00:00 2001 From: Jack Shedd Date: Tue, 22 Nov 2011 13:15:42 -0600 Subject: [PATCH 21/95] Updating model to reflect new Relationship --- instagram/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/instagram/models.py b/instagram/models.py index c709e699..4839e475 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -115,8 +115,9 @@ def __str__(self): class Relationship(ApiModel): - def __init__(self, incoming_status="none", outgoing_status="none"): + def __init__(self, incoming_status="none", outgoing_status="none", target_user_is_private=False): self.incoming_status = incoming_status self.outgoing_status = outgoing_status + self.target_user_is_private = target_user_is_private From 7cb5ca16e0760707d9b5fcb205207a429e9b3ce4 Mon Sep 17 00:00:00 2001 From: Jamie Curle Date: Sat, 2 Jul 2011 17:23:19 +0100 Subject: [PATCH 22/95] Updated the Location model to pass point in the kwargs as opposed the to args. This makes it available to the location object as location.point. When being passed in the args it was not being added to the Location class because the args were not being iterated over. --- instagram/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instagram/models.py b/instagram/models.py index 4839e475..6c0d2529 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -99,7 +99,7 @@ def object_from_dictionary(cls, entry): point = Point(entry.get('latitude'), entry.get('longitude')) location = cls(entry.get('id'), - point, + point=point, name=entry.get('name')) return location From 958f63267205271f02b5fe93bbf5527febc14b50 Mon Sep 17 00:00:00 2001 From: Mattias Gunneras Date: Fri, 25 Feb 2011 20:56:56 -0500 Subject: [PATCH 23/95] Added new json fixuture --- fixtures/location_recent_media.json | 2072 +++++++++++++++++++++++---- 1 file changed, 1825 insertions(+), 247 deletions(-) diff --git a/fixtures/location_recent_media.json b/fixtures/location_recent_media.json index ec5bb535..1751761d 100644 --- a/fixtures/location_recent_media.json +++ b/fixtures/location_recent_media.json @@ -1,376 +1,1954 @@ { - "pagination": {}, - "meta": { - "code": 200 - }, "data": [ { + "caption": { + "created_time": "1296751776", + "from": { + "full_name": "_Sasha Zvereva", + "id": "214995", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_214995_75sq_1294290728.jpg", + "username": "sanfrancisco" + }, + "id": "27014157", + "text": "Breakfast in the park and then off to the office" + }, + "comments": { + "count": 3, + "data": [ + { + "created_time": "1296753566", + "from": { + "full_name": "Lana P.", + "id": "814223", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_814223_75sq_1296856089.jpg", + "username": "heartsf" + }, + "id": "27023373", + "text": "I've been in the office since 7am. :(. What type of vino should I get for tonight? Can't wait to sip on some wine on a swing in south park this evening!!" + }, + { + "created_time": "1296776845", + "from": { + "full_name": "", + "id": "1034490", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1034490_75sq_1294697614.jpg", + "username": "cameragirl5d" + }, + "id": "27137642", + "text": "Nice:)" + }, + { + "created_time": "1296783501", + "from": { + "full_name": "Chelsea", + "id": "1810835", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1810835_75sq_1297065920.jpg", + "username": "imacookiemonster" + }, + "id": "27173636", + "text": "Love it :) please follow me" + } + ] + }, + "created_time": "1296751743", + "filter": "Earlybird", + "id": "22998901", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/03/93212f777adc49a0a33892e31af49465_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/03/93212f777adc49a0a33892e31af49465_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/03/93212f777adc49a0a33892e31af49465_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 15, + "data": [ + { + "full_name": "Irina Vass", + "id": "422714", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_422714_75sq_1298441779.jpg", + "username": "vasssf" + }, + { + "full_name": "", + "id": "1640871", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1640871_75sq_1295817240.jpg", + "username": "sacville" + }, + { + "full_name": "Andrew \ue04a Machulin", + "id": "16304", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_16304_75sq_1298241433.jpg", + "username": "amachulin" + }, + { + "full_name": "SpecialPixels Photography", + "id": "1089084", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1089084_75sq_1295560102.jpg", + "username": "specialpixels" + }, + { + "full_name": "Philippe Bouwen", + "id": "117158", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_117158_75sq_1297852026.jpg", + "username": "pbouwen" + }, + { + "full_name": "Aditya Prasad", + "id": "6230", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_6230_75sq_1296677663.jpg", + "username": "aditya" + }, + { + "full_name": "www.ikuphotos.com", + "id": "1104830", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1104830_75sq_1296455706.jpg", + "username": "ikulyl" + }, + { + "full_name": "Laura Twitter @lpow88", + "id": "15854", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_15854_75sq_1298595465.jpg", + "username": "lpow88" + } + ] + }, + "link": null, + "location": { + "id": "114", + "latitude": 37.78164992591033, + "longitude": -122.3938992619514, + "name": "South Park" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "_Sasha Zvereva", + "id": "214995", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_214995_75sq_1294290728.jpg", + "username": "sanfrancisco" + } + }, + { + "caption": null, + "comments": { + "count": 0, + "data": [] + }, + "created_time": "1296683671", + "filter": "Hefe", + "id": "22729355", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/02/b02b441e375947a1b3e7f14b87f9d00e_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/02/b02b441e375947a1b3e7f14b87f9d00e_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/02/b02b441e375947a1b3e7f14b87f9d00e_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 0, + "data": [] + }, + "link": "http://instagr.am/p/BWtKL/", + "location": { + "id": "114", + "latitude": 37.78164992591033, + "longitude": -122.3938992619514, + "name": "South Park" + }, + "tags": [], "type": "image", + "user": { + "full_name": "Olivia Vong", + "id": "183177", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_183177_75sq_1287451451.jpg", + "username": "oliviav" + } + }, + { + "caption": { + "created_time": "1296675993", + "from": { + "full_name": "", + "id": "590460", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_590460_75sq_1297129875.jpg", + "username": "bpmahl" + }, + "id": "26595552", + "text": "Lunch" + }, + "comments": { + "count": 2, + "data": [ + { + "created_time": "1296767659", + "from": { + "full_name": "_Sasha Zvereva", + "id": "214995", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_214995_75sq_1294290728.jpg", + "username": "sanfrancisco" + }, + "id": "27086536", + "text": "Where was that?" + }, + { + "created_time": "1296768591", + "from": { + "full_name": "", + "id": "590460", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_590460_75sq_1297129875.jpg", + "username": "bpmahl" + }, + "id": "27091011", + "text": "At Cafe Centro in South Park" + } + ] + }, + "created_time": "1296675964", + "filter": "X-Pro II", + "id": "22703578", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/02/683e178c1a26400ab291b2114076b205_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/02/683e178c1a26400ab291b2114076b205_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/02/683e178c1a26400ab291b2114076b205_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 0, + "data": [] + }, + "link": "http://instagr.am/p/BWm3a/", "location": { - "latitude": 37.769298430744279, - "id": "315", - "longitude": -122.4290335178376, - "name": "Duboce & Church MUNI Stop" + "id": "114", + "latitude": 37.78164992591033, + "longitude": -122.3938992619514, + "name": "South Park" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "", + "id": "590460", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_590460_75sq_1297129875.jpg", + "username": "bpmahl" + } + }, + { + "caption": { + "created_time": "1296673987", + "from": { + "full_name": "", + "id": "590460", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_590460_75sq_1297129875.jpg", + "username": "bpmahl" + }, + "id": "26586430", + "text": "Park" }, "comments": { "count": 0, "data": [] }, - "caption": null, - "link": "http://localhost:8000/p/FPdE/", + "created_time": "1296673985", + "filter": "Sutro", + "id": "22697199", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/02/119acde7a6024ea1a7714161b30480b2_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/02/119acde7a6024ea1a7714161b30480b2_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/02/119acde7a6024ea1a7714161b30480b2_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 1, + "data": [ + { + "full_name": "Erin Danielle Walker", + "id": "1059631", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1059631_75sq_1296944111.jpg", + "username": "omgsoerin" + } + ] + }, + "link": "http://instagr.am/p/BWlTv/", + "location": { + "id": "114", + "latitude": 37.78164992591033, + "longitude": -122.3938992619514, + "name": "South Park" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "", + "id": "590460", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_590460_75sq_1297129875.jpg", + "username": "bpmahl" + } + }, + { + "caption": { + "created_time": "1296602559", + "from": { + "full_name": "Robert Tillmann", + "id": "969", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_969_75sq_1289762676.jpg", + "username": "tillmannr" + }, + "id": "26188348", + "text": "South Park" + }, + "comments": { + "count": 0, + "data": [] + }, + "created_time": "1296602554", + "filter": "Lomo-fi", + "id": "22427368", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/01/2b5e9f2aec34460b87ec8ff87855e1c3_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/01/2b5e9f2aec34460b87ec8ff87855e1c3_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/01/2b5e9f2aec34460b87ec8ff87855e1c3_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 1, + "data": [ + { + "full_name": "", + "id": "1124341", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1124341_75sq_1293077591.jpg", + "username": "blooddrinker" + } + ] + }, + "link": "http://instagr.am/p/BVjbo/", + "location": { + "id": "114", + "latitude": 37.78164992591033, + "longitude": -122.3938992619514, + "name": "South Park" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "Robert Tillmann", + "id": "969", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_969_75sq_1289762676.jpg", + "username": "tillmannr" + } + }, + { + "caption": { + "created_time": "1296520666", + "from": { + "full_name": "Joe Bertino", + "id": "137994", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_137994_75sq_1287475371.jpg", + "username": "jjbert" + }, + "id": "25755531", + "text": "All the cool kids hang out in South Park, ain't that right @kweder?" + }, + "comments": { + "count": 4, + "data": [ + { + "created_time": "1296520809", + "from": { + "full_name": "Matt Kweder", + "id": "728070", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_728070_75sq_1292281425.jpg", + "username": "kweder" + }, + "id": "25756328", + "text": "Dam straight. Stop by next time you're strolling by #cloudtalk" + }, + { + "created_time": "1296521410", + "from": { + "full_name": "Joe Bertino", + "id": "137994", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_137994_75sq_1287475371.jpg", + "username": "jjbert" + }, + "id": "25759644", + "text": "Will do. Maybe later this week." + }, + { + "created_time": "1296594610", + "from": { + "full_name": "Matt Kweder", + "id": "728070", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_728070_75sq_1292281425.jpg", + "username": "kweder" + }, + "id": "26143162", + "text": "Stop by, definitely want to hear about your cross country adventure." + }, + { + "created_time": "1297027508", + "from": { + "full_name": "Jeff Whitney", + "id": "140211", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_140211_75sq_1290739688.jpg", + "username": "whiteknee" + }, + "id": "28542076", + "text": "Ha. Same shot from my stream! Perfect frame." + } + ] + }, + "created_time": "1296520590", + "filter": "Nashville", + "id": "22152194", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/31/fe4b2c1a814c4ffca1bb6db7fcec6a5f_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/31/fe4b2c1a814c4ffca1bb6db7fcec6a5f_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/31/fe4b2c1a814c4ffca1bb6db7fcec6a5f_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 3, + "data": [ + { + "full_name": "", + "id": "1761678", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1761678_75sq_1296498770.jpg", + "username": "cloudtalk" + }, + { + "full_name": "Rachel Shensa", + "id": "658569", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_658569_75sq_1289694952.jpg", + "username": "rshensa" + }, + { + "full_name": "Matt Kweder", + "id": "728070", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_728070_75sq_1292281425.jpg", + "username": "kweder" + } + ] + }, + "link": null, + "location": { + "id": "114", + "latitude": 37.78164992591033, + "longitude": -122.3938992619514, + "name": "South Park" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "Joe Bertino", + "id": "137994", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_137994_75sq_1287475371.jpg", + "username": "jjbert" + } + }, + { + "caption": { + "created_time": "1296059616", + "from": { + "full_name": "_Sasha Zvereva", + "id": "214995", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_214995_75sq_1294290728.jpg", + "username": "sanfrancisco" + }, + "id": "23202968", + "text": "Wine is not alcohol. @heartsf @vasssf @helender. South park happy hour coming soon" + }, + "comments": { + "count": 0, + "data": [] + }, + "created_time": "1296059496", + "filter": "Earlybird", + "id": "20443988", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/26/7f6772759c9d440ea48035e23e976daf_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/26/7f6772759c9d440ea48035e23e976daf_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/26/7f6772759c9d440ea48035e23e976daf_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 12, + "data": [ + { + "full_name": "Jens Bogaerts", + "id": "116762", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_116762_75sq_1297601040.jpg", + "username": "jebo88" + }, + { + "full_name": "SpecialPixels Photography", + "id": "1089084", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1089084_75sq_1295560102.jpg", + "username": "specialpixels" + }, + { + "full_name": "J. Zeynalov \ue512", + "id": "1184716", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1184716_75sq_1295076430.jpg", + "username": "zeynalov" + }, + { + "full_name": "Philippe Bouwen", + "id": "117158", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_117158_75sq_1297852026.jpg", + "username": "pbouwen" + }, + { + "full_name": "Ken", + "id": "1229204", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1229204_75sq_1294086092.jpg", + "username": "takenphotography" + }, + { + "full_name": "Yara", + "id": "1627875", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1627875_75sq_1298356386.jpg", + "username": "silverina" + }, + { + "full_name": "Lana P.", + "id": "814223", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_814223_75sq_1296856089.jpg", + "username": "heartsf" + }, + { + "full_name": "Brian Ng", + "id": "10102", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_10102_75sq_1295591546.jpg", + "username": "brianng" + } + ] + }, + "link": null, + "location": { + "id": "114", + "latitude": 37.78164992591033, + "longitude": -122.3938992619514, + "name": "South Park" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "_Sasha Zvereva", + "id": "214995", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_214995_75sq_1294290728.jpg", + "username": "sanfrancisco" + } + }, + { + "caption": { + "created_time": "1295991422", + "from": { + "full_name": "David Nottingham", + "id": "485494", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_485494_75sq_1290044060.jpg", + "username": "tommynotty" + }, + "id": "22902779", + "text": "Deep Blue" + }, + "comments": { + "count": 0, + "data": [] + }, + "created_time": "1295991393", + "filter": "Lomo-fi", + "id": "20232296", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/25/df09e0ce88034e4e83d2742cdddcff07_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/25/df09e0ce88034e4e83d2742cdddcff07_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/25/df09e0ce88034e4e83d2742cdddcff07_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 0, + "data": [] + }, + "link": "http://instagr.am/p/BNLho/", + "location": { + "id": "114", + "latitude": 37.78164992591033, + "longitude": -122.3938992619514, + "name": "South Park" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "David Nottingham", + "id": "485494", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_485494_75sq_1290044060.jpg", + "username": "tommynotty" + } + }, + { + "caption": { + "created_time": "1295990406", + "from": { + "full_name": "Jeff Whitney", + "id": "140211", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_140211_75sq_1290739688.jpg", + "username": "whiteknee" + }, + "id": "22898964", + "text": "Nice in these here parts. " + }, + "comments": { + "count": 0, + "data": [] + }, + "created_time": "1295990378", + "filter": "Lomo-fi", + "id": "20229456", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/25/8200844e20f14d95bcddc127af15683e_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/25/8200844e20f14d95bcddc127af15683e_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/25/8200844e20f14d95bcddc127af15683e_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 0, + "data": [] + }, + "link": "http://instagr.am/p/BNK1Q/", + "location": { + "id": "114", + "latitude": 37.78164992591033, + "longitude": -122.3938992619514, + "name": "South Park" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "Jeff Whitney", + "id": "140211", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_140211_75sq_1290739688.jpg", + "username": "whiteknee" + } + }, + { + "caption": { + "created_time": "1295740809", + "from": { + "full_name": "Gino Zahnd", + "id": "598401", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "gzahnd" + }, + "id": "21757651", + "text": "South Park Saturday" + }, + "comments": { + "count": 0, + "data": [] + }, + "created_time": "1295740790", + "filter": "Lord Kelvin", + "id": "19371807", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/22/03b93280189246caab4824909461efc3_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/22/03b93280189246caab4824909461efc3_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/22/03b93280189246caab4824909461efc3_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 1, + "data": [ + { + "full_name": "Jeremy Robins", + "id": "1766719", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1766719_75sq_1296527796.jpg", + "username": "jeremy_ibisdocs" + } + ] + }, + "link": null, + "location": { + "id": "114", + "latitude": 37.78164992591033, + "longitude": -122.3938992619514, + "name": "South Park" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "Gino Zahnd", + "id": "598401", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "gzahnd" + } + }, + { + "caption": { + "created_time": "1295729635", + "from": { + "full_name": "June Lin", + "id": "25096", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_25096_75sq_1286510305.jpg", + "username": "junelin" + }, + "id": "21703727", + "text": "Pretty pretty day" + }, + "comments": { + "count": 0, + "data": [] + }, + "created_time": "1295729624", + "filter": "Lomo-fi", + "id": "19324108", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/22/a496f938431f4b9b9e9cc9cf2a3b1cda_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/22/a496f938431f4b9b9e9cc9cf2a3b1cda_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/22/a496f938431f4b9b9e9cc9cf2a3b1cda_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 2, + "data": [ + { + "full_name": "\ue23cRod Begbie\ue23d", + "id": "3415", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_3415_75sq_1286423393.jpg", + "username": "rodbegbie" + }, + { + "full_name": "Fahd Butt", + "id": "37668", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_37668_75sq_1291399103.jpg", + "username": "fahdoo" + } + ] + }, + "link": "http://instagr.am/p/BJtzM/", + "location": { + "id": "114", + "latitude": 37.78164992591033, + "longitude": -122.3938992619514, + "name": "South Park" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "June Lin", + "id": "25096", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_25096_75sq_1286510305.jpg", + "username": "junelin" + } + }, + { + "caption": { + "created_time": "1295569139", + "from": { + "full_name": "Doctor Popular", + "id": "637420", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_637420_75sq_1294731332.jpg", + "username": "docpop" + }, + "id": "20961476", + "text": "The @Instagram crew rocking the collaboration scarf that @mermalady and I did. " + }, + "comments": { + "count": 3, + "data": [ + { + "created_time": "1295569616", + "from": { + "full_name": "imelda", + "id": "30332", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_30332_75sq_1297438005.jpg", + "username": "imelda" + }, + "id": "20963912", + "text": "Awesome scarf!" + }, + { + "created_time": "1295570621", + "from": { + "full_name": "\ue326 Arianna", + "id": "1355588", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1355588_75sq_1294757062.jpg", + "username": "streetstylish" + }, + "id": "20968854", + "text": "Brilliant scarf! That's amazing! xoxo" + }, + { + "created_time": "1295580149", + "from": { + "full_name": "Looklook@me.com", + "id": "539168", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_539168_75sq_1289160132.jpg", + "username": "itlbok" + }, + "id": "21002595", + "text": "That's so sweet.!! I want one.!!" + } + ] + }, + "created_time": "1295569103", + "filter": null, + "id": "18741071", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/3b94d1bc7f18490399675502041ac072_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/3b94d1bc7f18490399675502041ac072_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/3b94d1bc7f18490399675502041ac072_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 29, + "data": [ + { + "full_name": "BrandNew2ndHand", + "id": "792151", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_792151_75sq_1297676463.jpg", + "username": "boweryelectric" + }, + { + "full_name": "Wender .com.br", + "id": "604304", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_604304_75sq_1293907301.jpg", + "username": "wender" + }, + { + "full_name": "imelda", + "id": "30332", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_30332_75sq_1297438005.jpg", + "username": "imelda" + }, + { + "full_name": "", + "id": "1536931", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1536931_75sq_1295278252.jpg", + "username": "polwilyam" + }, + { + "full_name": "", + "id": "1436505", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1436505_75sq_1297493244.jpg", + "username": "zyjcad" + }, + { + "full_name": "Lana P.", + "id": "814223", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_814223_75sq_1296856089.jpg", + "username": "heartsf" + }, + { + "full_name": "", + "id": "474878", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_474878_75sq_1296722828.jpg", + "username": "naomi123chaar" + }, + { + "full_name": "Mitchell Raynard", + "id": "1538749", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1538749_75sq_1295439378.jpg", + "username": "grudge" + } + ] + }, + "link": "http://instagr.am/p/BHfdP/", + "location": { + "id": "114", + "latitude": 37.78164992591033, + "longitude": -122.3938992619514, + "name": "South Park" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "Doctor Popular", + "id": "637420", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_637420_75sq_1294731332.jpg", + "username": "docpop" + } + }, + { + "caption": { + "created_time": "1295568601", + "from": { + "full_name": "Doctor Popular", + "id": "637420", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_637420_75sq_1294731332.jpg", + "username": "docpop" + }, + "id": "20958748", + "text": "Invisible grilled cheese sandwich. W/ @sfslim" + }, + "comments": { + "count": 3, + "data": [ + { + "created_time": "1295570676", + "from": { + "full_name": "\ue326 Arianna", + "id": "1355588", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1355588_75sq_1294757062.jpg", + "username": "streetstylish" + }, + "id": "20969134", + "text": "I'm so jealous I wasn't there! Boohoo!" + }, + { + "created_time": "1295572853", + "from": { + "full_name": "Hideaki Ototsu", + "id": "833168", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_833168_75sq_1290872002.jpg", + "username": "windyrider" + }, + "id": "20979824", + "text": "Hoho!" + }, + { + "created_time": "1295643750", + "from": { + "full_name": "", + "id": "1496562", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1496562_75sq_1295316390.jpg", + "username": "mpesc0" + }, + "id": "21282276", + "text": "That guy is awesome" + } + ] + }, + "created_time": "1295568564", + "filter": null, + "id": "18739312", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/8979daa5fc2a4b5cbc383ec83b718131_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/8979daa5fc2a4b5cbc383ec83b718131_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/8979daa5fc2a4b5cbc383ec83b718131_5.jpg", + "width": 150 + } + }, "likes": { - "count": 0 + "count": 18, + "data": [ + { + "full_name": "_Sasha Zvereva", + "id": "214995", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_214995_75sq_1294290728.jpg", + "username": "sanfrancisco" + }, + { + "full_name": "imelda", + "id": "30332", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_30332_75sq_1297438005.jpg", + "username": "imelda" + }, + { + "full_name": "", + "id": "1436505", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1436505_75sq_1297493244.jpg", + "username": "zyjcad" + }, + { + "full_name": "Lana P.", + "id": "814223", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_814223_75sq_1296856089.jpg", + "username": "heartsf" + }, + { + "full_name": "Wender .com.br", + "id": "604304", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_604304_75sq_1293907301.jpg", + "username": "wender" + }, + { + "full_name": "BrandNew2ndHand", + "id": "792151", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_792151_75sq_1297676463.jpg", + "username": "boweryelectric" + }, + { + "full_name": "Carry van Bruggen", + "id": "300190", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_300190_75sq_1293414830.jpg", + "username": "carryeri" + }, + { + "full_name": "Steve Rhodes", + "id": "11687", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "tigerbeat" + } + ] + }, + "link": "http://instagr.am/p/BHfBw/", + "location": { + "id": "114", + "latitude": 37.78164992591033, + "longitude": -122.3938992619514, + "name": "South Park" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "Doctor Popular", + "id": "637420", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_637420_75sq_1294731332.jpg", + "username": "docpop" + } + }, + { + "caption": { + "created_time": "1295568266", + "from": { + "full_name": "Steve Rhodes", + "id": "11687", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "tigerbeat" + }, + "id": "20957022", + "text": "@Kevin holding IG scarf created by @docpop (those are his photos) @mermalady who is doing a kickstarter project to make a scarf each day in January " + }, + "comments": { + "count": 2, + "data": [ + { + "created_time": "1295568332", + "from": { + "full_name": "Steve Rhodes", + "id": "11687", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "tigerbeat" + }, + "id": "20957356", + "text": "#poprocket There is another unfiltered crop of this photo which shows more of the scarf. Best way to see most of the IG meetup photos is to click on South Park above" + }, + { + "created_time": "1295579267", + "from": { + "full_name": "lisa church", + "id": "263123", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_263123_75sq_1289807752.jpg", + "username": "lmc_sf" + }, + "id": "20998082", + "text": "i love this!" + } + ] }, - "created_time": "1288140787", + "created_time": "1295568141", + "filter": null, + "id": "18737868", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/26/e880cfd183514579bb8d88bae7db93f1_6.jpg", - "width": 480, - "height": 480 - }, - "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/26/e880cfd183514579bb8d88bae7db93f1_5.jpg", - "width": 150, - "height": 150 + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/d6f253f12c854d109e2af4ca27be3dbf_6.jpg", + "width": 306 }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/26/e880cfd183514579bb8d88bae7db93f1_7.jpg", - "width": 612, - "height": 612 + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/d6f253f12c854d109e2af4ca27be3dbf_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/d6f253f12c854d109e2af4ca27be3dbf_5.jpg", + "width": 150 } }, - "user_has_liked": false, - "id": "1374020", + "likes": { + "count": 3, + "data": [ + { + "full_name": "Edythe O.", + "id": "254831", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_254831_75sq_1297014733.jpg", + "username": "edythe" + }, + { + "full_name": "", + "id": "483744", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_483744_75sq_1293094885.jpg", + "username": "robotblood" + }, + { + "full_name": "Lala Law", + "id": "103427", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_103427_75sq_1286980583.jpg", + "username": "lalalaw" + } + ] + }, + "link": null, + "location": { + "id": "114", + "latitude": 37.78164992591033, + "longitude": -122.3938992619514, + "name": "South Park" + }, + "tags": [], + "type": "image", "user": { - "username": "kongelbak", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_280718_75sq_1288602280.jpg", - "id": "280718" + "full_name": "Steve Rhodes", + "id": "11687", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "tigerbeat" } }, { - "type": "image", - "location": { - "latitude": 37.769298430744279, - "id": "315", - "longitude": -122.4290335178376, - "name": "Duboce & Church MUNI Stop" + "caption": { + "created_time": "1295567992", + "from": { + "full_name": "Steve Rhodes", + "id": "11687", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "tigerbeat" + }, + "id": "20955637", + "text": "@Kevin holding instagram scarf created by @docpop @mermalady " }, "comments": { - "count": 2, + "count": 6, "data": [ { - "created_time": "1288110386", - "text": "Cleo's MO: flop on my foot, cry, profit.", + "created_time": "1295568037", "from": { - "username": "annekate", - "first_name": "Anne", - "last_name": "Halsall", - "type": "user", - "id": "26497" + "full_name": "Steve Rhodes", + "id": "11687", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "tigerbeat" }, - "id": "933780" + "id": "20955875", + "text": "Unfiltered to show all of that side of the scarf Best way to see most of the photos is to click on South Park above" }, { - "created_time": "1288114784", - "text": "That's supercute", + "created_time": "1295568188", "from": { - "username": "tristan", - "first_name": "Tristan", - "last_name": "O'Tierney", - "type": "user", - "id": "36385" + "full_name": "sharon wacker", + "id": "786457", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_786457_75sq_1292743253.jpg", + "username": "shronie" }, - "id": "937267" + "id": "20956613", + "text": "heard you take pics of sf...im in sf too! gonna follow ;)" + }, + { + "created_time": "1295568605", + "from": { + "full_name": "Steve Rhodes", + "id": "11687", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "tigerbeat" + }, + "id": "20958765", + "text": "Not all of that side but as much as possible. The other side incorporates the IG icon" + }, + { + "created_time": "1295569685", + "from": { + "full_name": "Claire", + "id": "29138", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_29138_75sq_1292329738.jpg", + "username": "montychick" + }, + "id": "20964266", + "text": "Wow!!!" + }, + { + "created_time": "1295573438", + "from": { + "full_name": "Steve Rhodes", + "id": "11687", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "tigerbeat" + }, + "id": "20982654", + "text": "Thanks @shronie @montychick" + }, + { + "created_time": "1295599869", + "from": { + "full_name": "BlankCanvas", + "id": "1368616", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "blankcanvas" + }, + "id": "21086498", + "text": "I think thats cool!!!" + } + ] + }, + "created_time": "1295567863", + "filter": null, + "id": "18736967", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/0d9a23abd10b41cf99a87d5750deeab5_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/0d9a23abd10b41cf99a87d5750deeab5_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/0d9a23abd10b41cf99a87d5750deeab5_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 5, + "data": [ + { + "full_name": "BlankCanvas", + "id": "1368616", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "blankcanvas" + }, + { + "full_name": "Edythe O.", + "id": "254831", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_254831_75sq_1297014733.jpg", + "username": "edythe" + }, + { + "full_name": "Claire", + "id": "29138", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_29138_75sq_1292329738.jpg", + "username": "montychick" + }, + { + "full_name": "sharon wacker", + "id": "786457", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_786457_75sq_1292743253.jpg", + "username": "shronie" + }, + { + "full_name": "Doctor Popular", + "id": "637420", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_637420_75sq_1294731332.jpg", + "username": "docpop" } ] }, + "link": null, + "location": { + "id": "114", + "latitude": 37.78164992591033, + "longitude": -122.3938992619514, + "name": "South Park" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "Steve Rhodes", + "id": "11687", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "tigerbeat" + } + }, + { "caption": { - "created_time": "1288110386", - "text": "Cleo's MO: flop on my foot, cry, profit.", + "created_time": "1295567454", "from": { - "username": "annekate", - "first_name": "Anne", - "last_name": "Halsall", - "type": "user", - "id": "26497" + "full_name": "Doctor Popular", + "id": "637420", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_637420_75sq_1294731332.jpg", + "username": "docpop" }, - "id": "933780" + "id": "20952680", + "text": "Had a good time at the unofficial Instagram meet-up in South Park: @tigerbeat @jeremybrooks @bubzlet @josh @kevin @sanfrancisco @mikeyk @shayne @heartsf @sfslim It was fun doing lunch and meeting folks, but next time we'll skip the lunch and just do a photo walk :)" }, - "link": "http://localhost:8000/p/FIb_/", - "likes": { - "count": 2 + "comments": { + "count": 9, + "data": [ + { + "created_time": "1295567539", + "from": { + "full_name": "Jeremy Brooks", + "id": "936151", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_936151_75sq_1295639392.jpg", + "username": "jeremybrooks" + }, + "id": "20953122", + "text": "That was fun. Looking forward to a walk!" + }, + { + "created_time": "1295569265", + "from": { + "full_name": "Lana P.", + "id": "814223", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_814223_75sq_1296856089.jpg", + "username": "heartsf" + }, + "id": "20962115", + "text": "Yes the walk. Maybe this Sunday?!?'" + }, + { + "created_time": "1295569273", + "from": { + "full_name": "_Sasha Zvereva", + "id": "214995", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_214995_75sq_1294290728.jpg", + "username": "sanfrancisco" + }, + "id": "20962152", + "text": "I'm in for the photowalk" + }, + { + "created_time": "1295570410", + "from": { + "full_name": "Doctor Popular", + "id": "637420", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_637420_75sq_1294731332.jpg", + "username": "docpop" + }, + "id": "20967785", + "text": "@heartsf & @sanfrancisco I'm moving this weekend, but might be down for something in the future. Y'all could still probably get some folks interested if you did it this weekend though." + }, + { + "created_time": "1295570648", + "from": { + "full_name": "_Sasha Zvereva", + "id": "214995", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_214995_75sq_1294290728.jpg", + "username": "sanfrancisco" + }, + "id": "20968986", + "text": "@heartsf I can't this weekend. How bout next" + }, + { + "created_time": "1295570873", + "from": { + "full_name": "Lana P.", + "id": "814223", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_814223_75sq_1296856089.jpg", + "username": "heartsf" + }, + "id": "20970107", + "text": "@sanfrancisco next Sunday sounds great. Lets make a flyer. Docpop will you help spread the word?!?" + }, + { + "created_time": "1295573163", + "from": { + "full_name": "Edythe O.", + "id": "254831", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_254831_75sq_1297014733.jpg", + "username": "edythe" + }, + "id": "20981356", + "text": "Hey, only three women! \ue521" + }, + { + "created_time": "1295575390", + "from": { + "full_name": "angie javier", + "id": "991458", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_991458_75sq_1293522149.jpg", + "username": "angelinej" + }, + "id": "20991911", + "text": "I live near SF." + } + ] }, - "created_time": "1288110325", + "created_time": "1295567294", + "filter": null, + "id": "18735102", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/26/4dd692be6f184c53b9a5b40da8685301_6.jpg", - "width": 480, - "height": 480 - }, - "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/26/4dd692be6f184c53b9a5b40da8685301_5.jpg", - "width": 150, - "height": 150 + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/5f48f3970f8746bc8e8940f0277b0092_6.jpg", + "width": 306 }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/26/4dd692be6f184c53b9a5b40da8685301_7.jpg", - "width": 612, - "height": 612 + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/5f48f3970f8746bc8e8940f0277b0092_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/5f48f3970f8746bc8e8940f0277b0092_5.jpg", + "width": 150 } }, - "user_has_liked": false, - "id": "1345279", + "likes": { + "count": 29, + "data": [ + { + "full_name": "", + "id": "653614", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_653614_75sq_1296526250.jpg", + "username": "vicmor" + }, + { + "full_name": "sharon wacker", + "id": "786457", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_786457_75sq_1292743253.jpg", + "username": "shronie" + }, + { + "full_name": "\ue326 Arianna", + "id": "1355588", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1355588_75sq_1294757062.jpg", + "username": "streetstylish" + }, + { + "full_name": "", + "id": "1122369", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1122369_75sq_1293960486.jpg", + "username": "lauriekeiko" + }, + { + "full_name": "Chelsea D.", + "id": "1339515", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1339515_75sq_1295657608.jpg", + "username": "chelsea518" + }, + { + "full_name": "Carry van Bruggen", + "id": "300190", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_300190_75sq_1293414830.jpg", + "username": "carryeri" + }, + { + "full_name": "Steve Rhodes", + "id": "11687", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "tigerbeat" + }, + { + "full_name": "B Z", + "id": "662466", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_662466_75sq_1292996770.jpg", + "username": "bubzlet" + } + ] + }, + "link": "http://instagr.am/p/BHd_-/", + "location": { + "id": "114", + "latitude": 37.78164992591033, + "longitude": -122.3938992619514, + "name": "South Park" + }, + "tags": [], + "type": "image", "user": { - "username": "annekate", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_26497_75sq_1286519271.jpg", - "id": "26497" + "full_name": "Doctor Popular", + "id": "637420", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_637420_75sq_1294731332.jpg", + "username": "docpop" } }, { - "type": "image", - "location": { - "latitude": 37.769298430744279, - "id": "315", - "longitude": -122.4290335178376, - "name": "Duboce & Church MUNI Stop" + "caption": { + "created_time": "1295566837", + "from": { + "full_name": "Steve Rhodes", + "id": "11687", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "tigerbeat" + }, + "id": "20949353", + "text": "@Kevin @mikeyk @shayne @docpop @Josh DocPop & @mermalady created the instagram scarf" }, "comments": { "count": 1, "data": [ { - "created_time": "1288061279", - "text": "Apparently not an all terrain train... Derailed", + "created_time": "1295566903", "from": { - "username": "garrett", - "first_name": "Garrett", - "last_name": "Albright", - "type": "user", - "id": "32" + "full_name": "Steve Rhodes", + "id": "11687", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "tigerbeat" }, - "id": "889921" + "id": "20949723", + "text": "Taken with #poprocket to thank the instagram team for bringing it back. Best way to see most of the meetup photos is to click on South Park above" } ] }, - "caption": { - "created_time": "1288061279", - "text": "Apparently not an all terrain train... Derailed", - "from": { - "username": "garrett", - "first_name": "Garrett", - "last_name": "Albright", - "type": "user", - "id": "32" - }, - "id": "889921" - }, - "link": "http://localhost:8000/p/E65l/", - "likes": { - "count": 2 - }, - "created_time": "1288061272", + "created_time": "1295566754", + "filter": null, + "id": "18733265", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/25/7cc48b1f20b14133aac50e2da517e4fa_6.jpg", - "width": 480, - "height": 480 - }, - "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/25/7cc48b1f20b14133aac50e2da517e4fa_5.jpg", - "width": 150, - "height": 150 + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/fff5ac053a044cfb9e2aa843cdfa3df4_6.jpg", + "width": 306 }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/25/7cc48b1f20b14133aac50e2da517e4fa_7.jpg", - "width": 612, - "height": 612 + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/fff5ac053a044cfb9e2aa843cdfa3df4_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/fff5ac053a044cfb9e2aa843cdfa3df4_5.jpg", + "width": 150 } }, - "user_has_liked": false, - "id": "1289829", + "likes": { + "count": 1, + "data": [ + { + "full_name": "sharon wacker", + "id": "786457", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_786457_75sq_1292743253.jpg", + "username": "shronie" + } + ] + }, + "link": null, + "location": { + "id": "114", + "latitude": 37.78164992591033, + "longitude": -122.3938992619514, + "name": "South Park" + }, + "tags": [], + "type": "image", "user": { - "username": "garrett", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_32_75sq.jpg", - "id": "32" + "full_name": "Steve Rhodes", + "id": "11687", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "tigerbeat" } }, { - "type": "image", - "location": { - "latitude": 37.769298430744279, - "id": "315", - "longitude": -122.4290335178376, - "name": "Duboce & Church MUNI Stop" + "caption": { + "created_time": "1295566616", + "from": { + "full_name": "Steve Rhodes", + "id": "11687", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "tigerbeat" + }, + "id": "20948089", + "text": "@Kevin @shayne @Josh wearing instagram scarf created by @docpop @mermalady " }, "comments": { "count": 1, "data": [ { - "created_time": "1287714601", - "text": "Scaredy Tree!!", + "created_time": "1295566630", "from": { - "username": "cinemaone", - "first_name": "Oscar", - "last_name": "V", - "type": "user", - "id": "124472" + "full_name": "Steve Rhodes", + "id": "11687", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "tigerbeat" }, - "id": "584630" + "id": "20948172", + "text": "Best way to see most of the photos is to click on South Park above" } ] }, - "caption": { - "created_time": "1287714601", - "text": "Scaredy Tree!!", - "from": { - "username": "cinemaone", - "first_name": "Oscar", - "last_name": "V", - "type": "user", - "id": "124472" - }, - "id": "584630" - }, - "link": "http://localhost:8000/p/DVnW/", - "likes": { - "count": 0 - }, - "created_time": "1287714600", + "created_time": "1295566522", + "filter": null, + "id": "18732449", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/21/1cfe528877564bd9b1b0e77fd1671a73_6.jpg", - "width": 480, - "height": 480 - }, - "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/21/1cfe528877564bd9b1b0e77fd1671a73_5.jpg", - "width": 150, - "height": 150 + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/de48e4b544af4b508bb8d96aa018f2f1_6.jpg", + "width": 306 }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/21/1cfe528877564bd9b1b0e77fd1671a73_7.jpg", - "width": 612, - "height": 612 + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/de48e4b544af4b508bb8d96aa018f2f1_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/de48e4b544af4b508bb8d96aa018f2f1_5.jpg", + "width": 150 } }, - "user_has_liked": false, - "id": "874966", + "likes": { + "count": 3, + "data": [ + { + "full_name": "", + "id": "1122369", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1122369_75sq_1293960486.jpg", + "username": "lauriekeiko" + }, + { + "full_name": "Doctor Popular", + "id": "637420", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_637420_75sq_1294731332.jpg", + "username": "docpop" + }, + { + "full_name": "", + "id": "1139868", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1139868_75sq_1293174563.jpg", + "username": "mermalady" + } + ] + }, + "link": null, + "location": { + "id": "114", + "latitude": 37.78164992591033, + "longitude": -122.3938992619514, + "name": "South Park" + }, + "tags": [], + "type": "image", "user": { - "username": "cinemaone", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_124472_75sq_1287902448.jpg", - "id": "124472" + "full_name": "Steve Rhodes", + "id": "11687", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "tigerbeat" } }, { - "type": "image", - "location": { - "latitude": 37.769298430744279, - "id": "315", - "longitude": -122.4290335178376, - "name": "Duboce & Church MUNI Stop" + "caption": { + "created_time": "1295566293", + "from": { + "full_name": "Steve Rhodes", + "id": "11687", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "tigerbeat" + }, + "id": "20946264", + "text": "@docpop taking a photo of @kevin @Josh @sanfrancisco @heartsf @shayne @sfslim (it was his idea to take a group photo)" }, "comments": { - "count": 1, + "count": 4, "data": [ { - "created_time": "1284129415", - "text": "On the N", + "created_time": "1295566335", + "from": { + "full_name": "Steve Rhodes", + "id": "11687", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "tigerbeat" + }, + "id": "20946499", + "text": "Best way to see most of the photos is to click on South Park above" + }, + { + "created_time": "1295567725", + "from": { + "full_name": "Steve Rhodes", + "id": "11687", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "tigerbeat" + }, + "id": "20954132", + "text": "Also @bubzlet & ame (not on IG)" + }, + { + "created_time": "1295569205", + "from": { + "full_name": "_Sasha Zvereva", + "id": "214995", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_214995_75sq_1294290728.jpg", + "username": "sanfrancisco" + }, + "id": "20961810", + "text": "Great meeting everyone!" + }, + { + "created_time": "1296450273", "from": { - "username": "nicole", - "first_name": "Nicole", - "last_name": "Schuetz", - "type": "user", - "id": "6" + "full_name": "Steve Rhodes", + "id": "11687", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "tigerbeat" }, - "id": "4131" + "id": "25388335", + "text": "#2011hearts (heartsf is in the photo)" } ] }, - "caption": null, - "link": "http://localhost:8000/p/0i/", - "likes": { - "count": 1 - }, - "created_time": "1284129353", + "created_time": "1295566200", + "filter": null, + "id": "18731325", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/09/10/1fb63f27b0f240178c33b320d4124c52_6.jpg", - "width": 480, - "height": 480 - }, - "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/09/10/1fb63f27b0f240178c33b320d4124c52_5.jpg", - "width": 150, - "height": 150 + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/95da08774e2f40e8981ca32785e6509e_6.jpg", + "width": 306 }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/09/10/1fb63f27b0f240178c33b320d4124c52_7.jpg", - "width": 612, - "height": 612 + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/95da08774e2f40e8981ca32785e6509e_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/95da08774e2f40e8981ca32785e6509e_5.jpg", + "width": 150 } }, - "user_has_liked": true, - "id": "3362", + "likes": { + "count": 1, + "data": [ + { + "full_name": "_Sasha Zvereva", + "id": "214995", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_214995_75sq_1294290728.jpg", + "username": "sanfrancisco" + } + ] + }, + "link": null, + "location": { + "id": "114", + "latitude": 37.78164992591033, + "longitude": -122.3938992619514, + "name": "South Park" + }, + "tags": [], + "type": "image", "user": { - "username": "nicole", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_6_75sq_1285365377.jpg", - "id": "6" + "full_name": "Steve Rhodes", + "id": "11687", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "tigerbeat" } }, { - "type": "image", - "location": { - "latitude": 37.769298430744279, - "id": "315", - "longitude": -122.4290335178376, - "name": "Duboce & Church MUNI Stop" + "caption": { + "created_time": "1295566125", + "from": { + "full_name": "Steve Rhodes", + "id": "11687", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "tigerbeat" + }, + "id": "20945360", + "text": "@docpop showing @sanfrancisco @heartsf how docpop.org displays his most recent #instagram photo in the corner #walden" }, "comments": { "count": 1, "data": [ { - "created_time": "1280976704", - "text": "Folks with equal rights signs on the muni", + "created_time": "1295566281", "from": { - "username": "mikeyk", - "first_name": "Mike", - "last_name": "Krieger!!", - "type": "user", - "id": "4" + "full_name": "_Sasha Zvereva", + "id": "214995", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_214995_75sq_1294290728.jpg", + "username": "sanfrancisco" }, - "id": "833" + "id": "20946200", + "text": "Awesome!!" } ] }, - "caption": null, - "link": "http://localhost:8000/p/LE/", - "likes": { - "count": 2 - }, - "created_time": "1280976678", + "created_time": "1295566013", + "filter": null, + "id": "18730645", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/08/04/0d95ad05a15047428090cebfcbd5e08d_6.jpg", - "width": 480, - "height": 480 - }, - "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/08/04/0d95ad05a15047428090cebfcbd5e08d_5.jpg", - "width": 150, - "height": 150 + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/deb3832b6b334f5ba692c2041c5ed217_6.jpg", + "width": 306 }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/08/04/0d95ad05a15047428090cebfcbd5e08d_7.jpg", - "width": 612, - "height": 612 + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/deb3832b6b334f5ba692c2041c5ed217_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/01/20/deb3832b6b334f5ba692c2041c5ed217_5.jpg", + "width": 150 } }, - "user_has_liked": false, - "id": "708", + "likes": { + "count": 3, + "data": [ + { + "full_name": "Edythe O.", + "id": "254831", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_254831_75sq_1297014733.jpg", + "username": "edythe" + }, + { + "full_name": "_Sasha Zvereva", + "id": "214995", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_214995_75sq_1294290728.jpg", + "username": "sanfrancisco" + }, + { + "full_name": "Lana P.", + "id": "814223", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_814223_75sq_1296856089.jpg", + "username": "heartsf" + } + ] + }, + "link": null, + "location": { + "id": "114", + "latitude": 37.78164992591033, + "longitude": -122.3938992619514, + "name": "South Park" + }, + "tags": [ + "instagram", + "walden" + ], + "type": "image", "user": { - "username": "mikeyk", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_4_75sq_1292324747_debug.jpg", - "id": "4" + "full_name": "Steve Rhodes", + "id": "11687", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "tigerbeat" } } - ] + ], + "meta": { + "code": 200 + }, + "pagination": { + "next_max_id": "18730645", + "next_url": "https://api.instagram.com/v1/locations/114/media/recent?max_id=18730645&client_id=75cdb99a33d74f74acc7928ae9408213" + } } \ No newline at end of file From d0bdf26608c090a84affeb42ca8e50419932b53b Mon Sep 17 00:00:00 2001 From: Mattias Gunneras Date: Fri, 25 Feb 2011 21:04:14 -0500 Subject: [PATCH 24/95] Added list of Users to Media.likes if available --- instagram/models.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/instagram/models.py b/instagram/models.py index 6c0d2529..e748b096 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -37,6 +37,10 @@ def object_from_dictionary(cls, entry): if 'user_has_liked' in entry: new_media.user_has_liked = entry['user_has_liked'] new_media.like_count = entry['likes']['count'] + new_media.likes = [] + if entry['likes'].has_key('data'): + for like in entry['likes']['data']: + new_media.likes.append(User.object_from_dictionary(like)) new_media.comment_count = entry['comments']['count'] new_media.comments = [] From e076fb459154477745b7a2e65e6110ed8b97cb4d Mon Sep 17 00:00:00 2001 From: Mattias Gunneras Date: Fri, 25 Feb 2011 21:21:45 -0500 Subject: [PATCH 25/95] Added caption class --- instagram/models.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/instagram/models.py b/instagram/models.py index e748b096..b861f9e3 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -80,11 +80,14 @@ def object_from_dictionary(cls, entry): text = entry['text'] created_at = timestamp_to_datetime(entry['created_time']) id = entry['id'] - return Comment(id=id, user=user, text=text, created_at=created_at) + return cls(id=id, user=user, text=text, created_at=created_at) def __unicode__(self): print "%s said \"%s\"" % (self.user.username, self.message) +class Caption(Comment): + pass + class Point(ApiModel): def __init__(self, latitude, longitude): self.latitude = latitude From 086d32d523be8b9dbef7b7bddf48fe452561f5ee Mon Sep 17 00:00:00 2001 From: Mattias Gunneras Date: Fri, 25 Feb 2011 21:23:30 -0500 Subject: [PATCH 26/95] Fixed bug where str representation of Comment returned None --- instagram/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instagram/models.py b/instagram/models.py index b861f9e3..97d6b08b 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -83,7 +83,7 @@ def object_from_dictionary(cls, entry): return cls(id=id, user=user, text=text, created_at=created_at) def __unicode__(self): - print "%s said \"%s\"" % (self.user.username, self.message) + return "%s said \"%s\"" % (self.user.username, self.message) class Caption(Comment): pass From c84d7643928b349878cddf429fb3933760eba5a9 Mon Sep 17 00:00:00 2001 From: Mattias Gunneras Date: Fri, 25 Feb 2011 21:29:14 -0500 Subject: [PATCH 27/95] Added Caption to Media model --- instagram/models.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/instagram/models.py b/instagram/models.py index 97d6b08b..5c002ccc 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -46,6 +46,10 @@ def object_from_dictionary(cls, entry): new_media.comments = [] for comment in entry['comments']['data']: new_media.comments.append(Comment.object_from_dictionary(comment)) + + new_media.caption = None + if entry['caption']: + new_media.caption = Caption.object_from_dictionary(entry['caption']) new_media.created_time = timestamp_to_datetime(entry['created_time']) From 4832523ec9fec8344c9b3125e5a5bff31a4d1363 Mon Sep 17 00:00:00 2001 From: Mattias Gunneras Date: Fri, 25 Feb 2011 22:45:59 -0500 Subject: [PATCH 28/95] Added method to fetch Media based on geography_id --- instagram/client.py | 6 ++++++ tests.py | 3 +++ 2 files changed, 9 insertions(+) diff --git a/instagram/client.py b/instagram/client.py index 8e664c4c..7e6d3c5e 100644 --- a/instagram/client.py +++ b/instagram/client.py @@ -137,6 +137,12 @@ def __init__(self, *args, **kwargs): root_class = Location, response_type = "entry") + geography_recent_media = bind_method( + path = "/geographies/{geography_id}", + accepts_parameters = MEDIA_ACCEPT_PARAMETERS + ["geography_id"], + root_class = Media, + paginates = True) + tag_recent_media = bind_method( path = "/tags/{tag_name}/media/recent", accepts_parameters = MEDIA_ACCEPT_PARAMETERS + ['tag_name'], diff --git a/tests.py b/tests.py index d645a530..e57feaf4 100755 --- a/tests.py +++ b/tests.py @@ -179,6 +179,9 @@ def test_change_relationship(self): # test shortcuts as well self.api.follow_user(user_id='10') self.api.unfollow_user(user_id='10') + + def test_geography_recent_media(self): + self.api.geography_recent_media(geography_id=1) if __name__ == '__main__': if not TEST_AUTH: From a1e8c0398bddc1eceaba4da544a6df8f3dff8e4c Mon Sep 17 00:00:00 2001 From: Mattias Gunneras Date: Fri, 25 Feb 2011 22:50:21 -0500 Subject: [PATCH 29/95] Whitespace cleanup tabs are now spaces. --- tests.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests.py b/tests.py index e57feaf4..09f5e469 100755 --- a/tests.py +++ b/tests.py @@ -172,14 +172,14 @@ def test_user_requested_by(self): self.api.user_followed_by() def test_user_incoming_requests(self): - self.api.user_incoming_requests() + self.api.user_incoming_requests() def test_change_relationship(self): - self.api.change_user_relationship(user_id=10, action="follow") - # test shortcuts as well - self.api.follow_user(user_id='10') - self.api.unfollow_user(user_id='10') - + self.api.change_user_relationship(user_id=10, action="follow") + # test shortcuts as well + self.api.follow_user(user_id='10') + self.api.unfollow_user(user_id='10') + def test_geography_recent_media(self): self.api.geography_recent_media(geography_id=1) From ed4397483fcc01dea81e8bdcc0fc5d85c91a4f75 Mon Sep 17 00:00:00 2001 From: Mattias Gunneras Date: Sun, 27 Feb 2011 19:26:56 -0500 Subject: [PATCH 30/95] Fix bug where Point didn't get added to Location --- instagram/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/instagram/models.py b/instagram/models.py index 5c002ccc..de635c80 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -46,7 +46,7 @@ def object_from_dictionary(cls, entry): new_media.comments = [] for comment in entry['comments']['data']: new_media.comments.append(Comment.object_from_dictionary(comment)) - + new_media.caption = None if entry['caption']: new_media.caption = Caption.object_from_dictionary(entry['caption']) @@ -89,7 +89,7 @@ def object_from_dictionary(cls, entry): def __unicode__(self): return "%s said \"%s\"" % (self.user.username, self.message) -class Caption(Comment): +class Caption(Comment): pass class Point(ApiModel): From 13c9df35ffa4ec421d68d538aca4cdcc0e80c5d3 Mon Sep 17 00:00:00 2001 From: Mattias Gunneras Date: Sun, 27 Feb 2011 20:29:30 -0500 Subject: [PATCH 31/95] Added filter property to Media model --- instagram/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/instagram/models.py b/instagram/models.py index de635c80..1cad2b5f 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -61,6 +61,8 @@ def object_from_dictionary(cls, entry): new_media.caption = Comment.object_from_dictionary(entry['caption']) new_media.link = entry['link'] + + new_media.filter = entry['filter'] return new_media From 557678cc0eff553cd369676be18937e62d34e19a Mon Sep 17 00:00:00 2001 From: Mattias Gunneras Date: Sun, 27 Feb 2011 21:23:05 -0500 Subject: [PATCH 32/95] Fixed bug in geographies url endpoint --- instagram/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instagram/client.py b/instagram/client.py index 7e6d3c5e..6756968f 100644 --- a/instagram/client.py +++ b/instagram/client.py @@ -138,7 +138,7 @@ def __init__(self, *args, **kwargs): response_type = "entry") geography_recent_media = bind_method( - path = "/geographies/{geography_id}", + path = "/geographies/{geography_id}/media/recent", accepts_parameters = MEDIA_ACCEPT_PARAMETERS + ["geography_id"], root_class = Media, paginates = True) From ed0bccf5de691b50f3dfc3a0fd0a4f4cd53a63f7 Mon Sep 17 00:00:00 2001 From: Mattias Gunneras Date: Mon, 28 Feb 2011 09:27:20 -0500 Subject: [PATCH 33/95] Added fixture for geography query --- fixtures/geography_recent_media.json | 1142 ++++++++++++++++++++++++++ 1 file changed, 1142 insertions(+) create mode 100644 fixtures/geography_recent_media.json diff --git a/fixtures/geography_recent_media.json b/fixtures/geography_recent_media.json new file mode 100644 index 00000000..6619e639 --- /dev/null +++ b/fixtures/geography_recent_media.json @@ -0,0 +1,1142 @@ +{ + "data": [ + { + "caption": { + "created_time": "1298688893", + "from": { + "full_name": "\ue32eLuzmila\ue32e", + "id": "1056886", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1056886_75sq_1297088163.jpg", + "username": "lulu_love17" + }, + "id": "38108230", + "text": "Ocean Drive \ue43e \ue32a" + }, + "comments": { + "count": 0, + "data": [] + }, + "created_time": "1298688799", + "filter": "Hefe", + "id": "31000411", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/ab5beb5454ea4c85b7fd90ed02bd6d0c_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/ab5beb5454ea4c85b7fd90ed02bd6d0c_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/ab5beb5454ea4c85b7fd90ed02bd6d0c_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 2, + "data": [ + { + "full_name": "Withinmysol", + "id": "1068656", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1068656_75sq_1295187999.jpg", + "username": "withinmysol" + }, + { + "full_name": "", + "id": "1397022", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1397022_75sq_1296406549.jpg", + "username": "erynn" + } + ] + }, + "link": "http://instagr.am/p/B2Qdb/", + "location": { + "id": "1313027", + "latitude": 25.775409, + "longitude": -80.134048000000007, + "name": "South Beach, Fl" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "\ue32eLuzmila\ue32e", + "id": "1056886", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1056886_75sq_1297088163.jpg", + "username": "lulu_love17" + } + }, + { + "caption": { + "created_time": "1298674022", + "from": { + "full_name": "Kent Nguyen", + "id": "33287", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_33287_75sq_1287025465.jpg", + "username": "kentnguyen" + }, + "id": "38006529", + "text": "Gay." + }, + "comments": { + "count": 0, + "data": [] + }, + "created_time": "1298674017", + "filter": "Inkwell", + "id": "30927855", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/2ada6e0b96074eaf8b6d7f939febeb58_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/2ada6e0b96074eaf8b6d7f939febeb58_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/2ada6e0b96074eaf8b6d7f939febeb58_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 0, + "data": [] + }, + "link": "http://instagr.am/p/B1-vv/", + "location": { + "id": "671519", + "latitude": 25.782869000000002, + "longitude": -80.130409999999998, + "name": "Palace Bar" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "Kent Nguyen", + "id": "33287", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_33287_75sq_1287025465.jpg", + "username": "kentnguyen" + } + }, + { + "caption": { + "created_time": "1298673435", + "from": { + "full_name": "Jerid Choen", + "id": "2198895", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_2198895_75sq_1298664722.jpg", + "username": "fiveski" + }, + "id": "38002786", + "text": "A whole lotta coffee " + }, + "comments": { + "count": 1, + "data": [ + { + "created_time": "1298674595", + "from": { + "full_name": "", + "id": "790053", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_790053_75sq_1290570046.jpg", + "username": "tommycalvo" + }, + "id": "38009996", + "text": "Wired just looking at this." + } + ] + }, + "created_time": "1298673399", + "filter": "Lomo-fi", + "id": "30925082", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/80dac67f35f94eb79ee4d40654127b04_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/80dac67f35f94eb79ee4d40654127b04_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/80dac67f35f94eb79ee4d40654127b04_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 0, + "data": [] + }, + "link": "http://instagr.am/p/B1-Ea/", + "location": { + "id": "402760", + "latitude": 25.799709, + "longitude": -80.127409999999998, + "name": "Gansevoort Hotel" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "Jerid Choen", + "id": "2198895", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_2198895_75sq_1298664722.jpg", + "username": "fiveski" + } + }, + { + "caption": { + "created_time": "1298672337", + "from": { + "full_name": "Jerid Choen", + "id": "2198895", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_2198895_75sq_1298664722.jpg", + "username": "fiveski" + }, + "id": "37996060", + "text": "Hello! Kiddies!" + }, + "comments": { + "count": 1, + "data": [ + { + "created_time": "1298674541", + "from": { + "full_name": "", + "id": "790053", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_790053_75sq_1290570046.jpg", + "username": "tommycalvo" + }, + "id": "38009644", + "text": "Knots! Love the get up." + } + ] + }, + "created_time": "1298672333", + "filter": "Apollo", + "id": "30920336", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/1db76b6bd85d45d9b5def24ab311f4cc_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/1db76b6bd85d45d9b5def24ab311f4cc_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/1db76b6bd85d45d9b5def24ab311f4cc_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 0, + "data": [] + }, + "link": "http://instagr.am/p/B186Q/", + "location": { + "id": "81620", + "latitude": 25.797829, + "longitude": -80.127860999999996, + "name": "W Hotel - South Beach" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "Jerid Choen", + "id": "2198895", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_2198895_75sq_1298664722.jpg", + "username": "fiveski" + } + }, + { + "caption": { + "created_time": "1298670942", + "from": { + "full_name": "Fabian Alcantara", + "id": "16073", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_16073_75sq_1289251767.jpg", + "username": "fabuloso" + }, + "id": "37987914", + "text": "Perfect blue skies" + }, + "comments": { + "count": 0, + "data": [] + }, + "created_time": "1298670941", + "filter": "X-Pro II", + "id": "30914462", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/9838b83154a44c1f9e13366fbd97496e_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/9838b83154a44c1f9e13366fbd97496e_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/9838b83154a44c1f9e13366fbd97496e_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 0, + "data": [] + }, + "link": "http://instagr.am/p/B17ee/", + "location": { + "id": "499461", + "latitude": 25.783766, + "longitude": -80.130262999999999, + "name": "South Beach" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "Fabian Alcantara", + "id": "16073", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_16073_75sq_1289251767.jpg", + "username": "fabuloso" + } + }, + { + "caption": { + "created_time": "1298668492", + "from": { + "full_name": "Kent Nguyen", + "id": "33287", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_33287_75sq_1287025465.jpg", + "username": "kentnguyen" + }, + "id": "37974294", + "text": "The epitome of beach drinking" + }, + "comments": { + "count": 0, + "data": [] + }, + "created_time": "1298668487", + "filter": "X-Pro II", + "id": "30904041", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/634e57abbbba46adbff9950574d8b02f_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/634e57abbbba46adbff9950574d8b02f_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/634e57abbbba46adbff9950574d8b02f_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 0, + "data": [] + }, + "link": "http://instagr.am/p/B147p/", + "location": { + "id": "505254", + "latitude": 25.782665000000001, + "longitude": -80.128191999999999, + "name": "The Beach at 12th" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "Kent Nguyen", + "id": "33287", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_33287_75sq_1287025465.jpg", + "username": "kentnguyen" + } + }, + { + "caption": { + "created_time": "1298667678", + "from": { + "full_name": "Kent Nguyen", + "id": "33287", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_33287_75sq_1287025465.jpg", + "username": "kentnguyen" + }, + "id": "37970103", + "text": "Warmth" + }, + "comments": { + "count": 0, + "data": [] + }, + "created_time": "1298667637", + "filter": "Toaster", + "id": "30900564", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/5cddd82d19004e10a48ce3f79041d3c3_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/5cddd82d19004e10a48ce3f79041d3c3_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/5cddd82d19004e10a48ce3f79041d3c3_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 1, + "data": [ + { + "full_name": "Aaron Lewis", + "id": "10515", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_10515_75sq_1286421290.jpg", + "username": "aaronglewis" + } + ] + }, + "link": "http://instagr.am/p/B14FU/", + "location": { + "id": "505254", + "latitude": 25.782665000000001, + "longitude": -80.128191999999999, + "name": "The Beach at 12th" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "Kent Nguyen", + "id": "33287", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_33287_75sq_1287025465.jpg", + "username": "kentnguyen" + } + }, + { + "caption": { + "created_time": "1298666842", + "from": { + "full_name": "", + "id": "336510", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_336510_75sq_1291233229.jpg", + "username": "jdecker" + }, + "id": "37966056", + "text": "Miami" + }, + "comments": { + "count": 0, + "data": [] + }, + "created_time": "1298666821", + "filter": "Earlybird", + "id": "30897420", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/2fb2c2c0463b459eb5667ff920567864_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/2fb2c2c0463b459eb5667ff920567864_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/2fb2c2c0463b459eb5667ff920567864_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 0, + "data": [] + }, + "link": "http://instagr.am/p/B13UM/", + "location": { + "id": "289408", + "latitude": 25.784192999999998, + "longitude": -80.130199000000005, + "name": "South Beach Ocean" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "", + "id": "336510", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_336510_75sq_1291233229.jpg", + "username": "jdecker" + } + }, + { + "caption": { + "created_time": "1298665180", + "from": { + "full_name": "", + "id": "2197230", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "cmcrosby" + }, + "id": "37957766", + "text": "Miami" + }, + "comments": { + "count": 0, + "data": [] + }, + "created_time": "1298665147", + "filter": "Earlybird", + "id": "30890903", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/03d8530c3b284d96925c9d1c6ed039e3_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/03d8530c3b284d96925c9d1c6ed039e3_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/03d8530c3b284d96925c9d1c6ed039e3_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 0, + "data": [] + }, + "link": "http://instagr.am/p/B11uX/", + "location": { + "id": "356866", + "latitude": 25.815430900286731, + "longitude": -80.122459530830383, + "name": "Four Points By Sheraton Miami Beach" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "", + "id": "2197230", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "cmcrosby" + } + }, + { + "caption": { + "created_time": "1298665069", + "from": { + "full_name": "", + "id": "2197230", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "cmcrosby" + }, + "id": "37957191", + "text": "Just chillin'" + }, + "comments": { + "count": 0, + "data": [] + }, + "created_time": "1298665044", + "filter": "Earlybird", + "id": "30890496", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/215b5c264974433e9cd90a6ab6b87717_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/215b5c264974433e9cd90a6ab6b87717_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/215b5c264974433e9cd90a6ab6b87717_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 0, + "data": [] + }, + "link": "http://instagr.am/p/B11oA/", + "location": { + "id": "356866", + "latitude": 25.815430900286731, + "longitude": -80.122459530830383, + "name": "Four Points By Sheraton Miami Beach" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "", + "id": "2197230", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "cmcrosby" + } + }, + { + "caption": { + "created_time": "1298663506", + "from": { + "full_name": "Tyler Hunt", + "id": "188651", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "tylerhunt" + }, + "id": "37949493", + "text": "Winter in Florida" + }, + "comments": { + "count": 0, + "data": [] + }, + "created_time": "1298663503", + "filter": "X-Pro II", + "id": "30884692", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/693823b891534f2d8de5f7b6ed5295ab_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/693823b891534f2d8de5f7b6ed5295ab_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/693823b891534f2d8de5f7b6ed5295ab_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 0, + "data": [] + }, + "link": "http://instagr.am/p/B10NU/", + "location": { + "id": "285438", + "latitude": 25.790564, + "longitude": -80.136550999999997, + "name": "Lincoln Road Mall" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "Tyler Hunt", + "id": "188651", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "tylerhunt" + } + }, + { + "caption": { + "created_time": "1298661746", + "from": { + "full_name": "Eric Allam", + "id": "5733", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_5733_75sq_1286458838.jpg", + "username": "rubymaverick" + }, + "id": "37940900", + "text": "Walking down Lincoln road" + }, + "comments": { + "count": 0, + "data": [] + }, + "created_time": "1298661723", + "filter": "Earlybird", + "id": "30878142", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/0d19be935a6b4c1a906334c37ad23851_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/0d19be935a6b4c1a906334c37ad23851_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/0d19be935a6b4c1a906334c37ad23851_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 1, + "data": [ + { + "full_name": "", + "id": "333049", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "eklosterman" + } + ] + }, + "link": "http://instagr.am/p/B1ym-/", + "location": { + "id": "285438", + "latitude": 25.790564, + "longitude": -80.136550999999997, + "name": "Lincoln Road Mall" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "Eric Allam", + "id": "5733", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_5733_75sq_1286458838.jpg", + "username": "rubymaverick" + } + }, + { + "caption": { + "created_time": "1298659226", + "from": { + "full_name": "", + "id": "2195617", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "misssparker" + }, + "id": "37928792", + "text": "The Standard" + }, + "comments": { + "count": 0, + "data": [] + }, + "created_time": "1298659215", + "filter": "Toaster", + "id": "30868862", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/09a70957c03e486a93648d71540df065_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/09a70957c03e486a93648d71540df065_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/09a70957c03e486a93648d71540df065_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 0, + "data": [] + }, + "link": "http://instagr.am/p/B1wV-/", + "location": { + "id": "220744", + "latitude": 25.792287999999999, + "longitude": -80.149286000000004, + "name": "The Standard Miami" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "", + "id": "2195617", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "misssparker" + } + }, + { + "caption": null, + "comments": { + "count": 1, + "data": [ + { + "created_time": "1298671739", + "from": { + "full_name": "Stephany Ospina", + "id": "236852", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_236852_75sq_1292203189.jpg", + "username": "stephospina" + }, + "id": "37992465", + "text": "\"fitted attire\" lol" + } + ] + }, + "created_time": "1298658189", + "filter": null, + "id": "30864690", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/ba365bc2e90049ae826db46468b0c4d1_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/ba365bc2e90049ae826db46468b0c4d1_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/ba365bc2e90049ae826db46468b0c4d1_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 1, + "data": [ + { + "full_name": "Andrea Cupcake", + "id": "1130204", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1130204_75sq_1293193717.jpg", + "username": "koshercupcake" + } + ] + }, + "link": "http://instagr.am/p/B1vUy/", + "location": { + "id": "632073", + "latitude": 25.791796000000001, + "longitude": -80.138589999999994, + "name": "Lucky Strikes Lanes & Lounge" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "Roberto Martinez", + "id": "124346", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_124346_75sq_1296666430.jpg", + "username": "amazinrobert" + } + }, + { + "caption": { + "created_time": "1298656945", + "from": { + "full_name": "", + "id": "2165267", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_2165267_75sq_1298657401.jpg", + "username": "nicolediodonet" + }, + "id": "37916530", + "text": "ART" + }, + "comments": { + "count": 0, + "data": [] + }, + "created_time": "1298656642", + "filter": "Lomo-fi", + "id": "30858567", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/0c533b470ac74ddd827625f4274290c9_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/0c533b470ac74ddd827625f4274290c9_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/0c533b470ac74ddd827625f4274290c9_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 0, + "data": [] + }, + "link": null, + "location": { + "id": "958155", + "latitude": 25.790111, + "longitude": -80.136858000000004, + "name": "Art Center South Florida" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "", + "id": "2165267", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_2165267_75sq_1298657401.jpg", + "username": "nicolediodonet" + } + }, + { + "caption": { + "created_time": "1298656311", + "from": { + "full_name": "", + "id": "2165267", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_2165267_75sq_1298657401.jpg", + "username": "nicolediodonet" + }, + "id": "37913025", + "text": "Sea Creatures " + }, + "comments": { + "count": 0, + "data": [] + }, + "created_time": "1298656242", + "filter": "Lomo-fi", + "id": "30856946", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/9029c7277319443e9a59ca7d67615aae_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/9029c7277319443e9a59ca7d67615aae_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/9029c7277319443e9a59ca7d67615aae_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 0, + "data": [] + }, + "link": null, + "location": { + "id": "958155", + "latitude": 25.790111, + "longitude": -80.136858000000004, + "name": "Art Center South Florida" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "", + "id": "2165267", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_2165267_75sq_1298657401.jpg", + "username": "nicolediodonet" + } + }, + { + "caption": { + "created_time": "1298656177", + "from": { + "full_name": "Tanu Suri", + "id": "215250", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_215250_75sq_1287677743.jpg", + "username": "tanu" + }, + "id": "37912233", + "text": "Beautiful afternoon in Miami!" + }, + "comments": { + "count": 0, + "data": [] + }, + "created_time": "1298656131", + "filter": "Lomo-fi", + "id": "30856500", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/348b69e779f14426b47aa0472b839f96_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/348b69e779f14426b47aa0472b839f96_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/348b69e779f14426b47aa0472b839f96_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 0, + "data": [] + }, + "link": "http://instagr.am/p/B1tU0/", + "location": { + "id": "1517948", + "latitude": 25.851096999999999, + "longitude": -80.119667000000007, + "name": "Deauville Hotel" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "Tanu Suri", + "id": "215250", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_215250_75sq_1287677743.jpg", + "username": "tanu" + } + }, + { + "caption": { + "created_time": "1298655780", + "from": { + "full_name": "", + "id": "2197230", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "cmcrosby" + }, + "id": "37909881", + "text": "Lincoln road, south beach" + }, + "comments": { + "count": 0, + "data": [] + }, + "created_time": "1298655778", + "filter": "Earlybird", + "id": "30855064", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/f672982696654db18e053294766a9565_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/f672982696654db18e053294766a9565_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/f672982696654db18e053294766a9565_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 0, + "data": [] + }, + "link": "http://instagr.am/p/B1s-Y/", + "location": { + "id": "433631", + "latitude": 25.790199999999999, + "longitude": -80.137799999999999, + "name": "Pasha's" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "", + "id": "2197230", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/anonymousUser.jpg", + "username": "cmcrosby" + } + }, + { + "caption": { + "created_time": "1298655661", + "from": { + "full_name": "", + "id": "2165267", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_2165267_75sq_1298657401.jpg", + "username": "nicolediodonet" + }, + "id": "37909161", + "text": "Morning fireworks " + }, + "comments": { + "count": 0, + "data": [] + }, + "created_time": "1298655620", + "filter": "Lomo-fi", + "id": "30854426", + "images": { + "low_resolution": { + "height": 306, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/12053a3e571f4c26951f573f13a7adf6_6.jpg", + "width": 306 + }, + "standard_resolution": { + "height": 612, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/12053a3e571f4c26951f573f13a7adf6_7.jpg", + "width": 612 + }, + "thumbnail": { + "height": 150, + "url": "http://distillery.s3.amazonaws.com/media/2011/02/25/12053a3e571f4c26951f573f13a7adf6_5.jpg", + "width": 150 + } + }, + "likes": { + "count": 1, + "data": [ + { + "full_name": "", + "id": "1639851", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1639851_75sq_1295812928.jpg", + "username": "diorsantos" + } + ] + }, + "link": null, + "location": { + "id": "567181", + "latitude": 25.8039094166182, + "longitude": -80.128097534179688, + "name": "South Beach, Miami" + }, + "tags": [], + "type": "image", + "user": { + "full_name": "", + "id": "2165267", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_2165267_75sq_1298657401.jpg", + "username": "nicolediodonet" + } + } + ], + "meta": { + "code": 200 + }, + "pagination": { + "next_max_id": "30854426", + "next_url": "https://api.instagram.com/v1/geographies/106/media/recent?max_id=30854426&client_id=XXXXXX" + } +} From 38b0e2187e83e2cdb96ea3c31f753a4e1d8ee01a Mon Sep 17 00:00:00 2001 From: Mattias Gunneras Date: Sun, 10 Apr 2011 12:37:05 -0400 Subject: [PATCH 34/95] fixed models --- instagram/models.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/instagram/models.py b/instagram/models.py index 1cad2b5f..1a1d5c85 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -53,7 +53,7 @@ def object_from_dictionary(cls, entry): new_media.created_time = timestamp_to_datetime(entry['created_time']) - if entry['location']: + if entry['location'] and hasattr(entry, 'id'): new_media.location = Location.object_from_dictionary(entry['location']) new_media.caption = None @@ -61,7 +61,7 @@ def object_from_dictionary(cls, entry): new_media.caption = Comment.object_from_dictionary(entry['caption']) new_media.link = entry['link'] - + new_media.filter = entry['filter'] return new_media @@ -111,9 +111,9 @@ def object_from_dictionary(cls, entry): if 'latitude' in entry: point = Point(entry.get('latitude'), entry.get('longitude')) - location = cls(entry.get('id'), + location = cls(entry.get('id', 0), point=point, - name=entry.get('name')) + name=entry.get('name', '')) return location class User(ApiModel): From 793111049c9552830a31690e56db2f93d298f99e Mon Sep 17 00:00:00 2001 From: Mattias Gunneras Date: Mon, 11 Apr 2011 01:35:15 -0400 Subject: [PATCH 35/95] Bugfix. check key not attribute of dict --- instagram/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instagram/models.py b/instagram/models.py index 1a1d5c85..711a50de 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -53,7 +53,7 @@ def object_from_dictionary(cls, entry): new_media.created_time = timestamp_to_datetime(entry['created_time']) - if entry['location'] and hasattr(entry, 'id'): + if entry['location'] and entry.has_key('id'): new_media.location = Location.object_from_dictionary(entry['location']) new_media.caption = None From 9e621468fc2acac849255176c0910dd7f5b5731a Mon Sep 17 00:00:00 2001 From: Mike Krieger Date: Thu, 23 Feb 2012 17:42:24 -0800 Subject: [PATCH 36/95] Handle if filter is missing --- instagram/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instagram/models.py b/instagram/models.py index 711a50de..9e6fd8aa 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -62,7 +62,7 @@ def object_from_dictionary(cls, entry): new_media.link = entry['link'] - new_media.filter = entry['filter'] + new_media.filter = entry.get('filter') return new_media From 2f8bedad4bc5d94ea6397077489fefee91681c11 Mon Sep 17 00:00:00 2001 From: Mike Krieger Date: Thu, 23 Feb 2012 17:54:27 -0800 Subject: [PATCH 37/95] Making the mock http class compatible with the kwarg passed in --- instagram/oauth2.py | 9 ++++----- tests.py | 10 +++++++--- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/instagram/oauth2.py b/instagram/oauth2.py index ad6f8204..1f3cc765 100644 --- a/instagram/oauth2.py +++ b/instagram/oauth2.py @@ -3,7 +3,6 @@ from httplib2 import Http import mimetypes - class OAuth2AuthExchangeError(Exception): def __init__(self, description): self.description = description @@ -124,10 +123,10 @@ def post_request(self, path, **kwargs): return self.make_request(self.prepare_request("POST", path, kwargs)) def _full_url(self, path, include_secret=False): - return "%s://%s%s%s%s" % (self.api.protocol, - self.api.host, - self.api.base_path, - path, + return "%s://%s%s%s%s" % (self.api.protocol, + self.api.host, + self.api.base_path, + path, self._auth_query(include_secret)) def _full_url_with_params(self, path, params, include_secret=False): diff --git a/tests.py b/tests.py index 09f5e469..8d9cbac2 100755 --- a/tests.py +++ b/tests.py @@ -16,6 +16,10 @@ redirect_uri = "http://example.com" class MockHttp(object): + + def __init__(self, *args, **kwargs): + pass + def request(self, url, method="GET", body=None, headers={}): fail_state = { 'status':'400' @@ -30,7 +34,7 @@ def request(self, url, method="GET", body=None, headers={}): 'status': '200', 'content-location':'http://example.com/redirect/login' }, None - + if not 'access_token' in options and not 'client_id' in options: fn_name += '_unauthorized' if 'self' in url and not 'access_token' in options: @@ -177,9 +181,9 @@ def test_user_incoming_requests(self): def test_change_relationship(self): self.api.change_user_relationship(user_id=10, action="follow") # test shortcuts as well - self.api.follow_user(user_id='10') + self.api.follow_user(user_id='10') self.api.unfollow_user(user_id='10') - + def test_geography_recent_media(self): self.api.geography_recent_media(geography_id=1) From 8e8ccbfc68a60f2013cea4db826ffa2c3442d1a2 Mon Sep 17 00:00:00 2001 From: Mike Krieger Date: Thu, 23 Feb 2012 17:57:18 -0800 Subject: [PATCH 38/95] Version bump and README update --- README.md | 11 +++++------ setup.py | 3 +-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 6a7bf07d..d196444d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A Python client for the Instagram REST and Search APIs Installation ----- -pip install instagram +python setup.py install Requires ----- @@ -18,8 +18,8 @@ updates, and news about the Instagram gem. Obtaining an access token ----- -You can use the provided get_access_token.py script to obtain an access token for yourself. -It will prompt you for your app's Client ID, Client Secret, and Redirect URI, +You can use the provided get_access_token.py script to obtain an access token for yourself. +It will prompt you for your app's Client ID, Client Secret, and Redirect URI, and walk you through instructions for getting your own access token for your app. Usage @@ -36,9 +36,8 @@ Sample app ------ We also provide a one-file sample app using bottle (you'll have to 'pip install bottle' first). To try it out: - * Set your redirect URI to 'http://localhost:8515/outh_callback' in your dev profile - * Open up sample\_app.py, update it with your client\_id and secret, and set redirect URI to 'http://localhost:8515/outh_callback' + * Set your redirect URI to 'http://localhost:8515/oauth_callback' in your dev profile + * Open up sample\_app.py, update it with your client\_id and secret, and set redirect URI to 'http://localhost:8515/oauth_callback' * Run the file; it will host a local server on port 8515. * Try visiting http://localhost:8515 in your browser - diff --git a/setup.py b/setup.py index 2aaf1c0b..62d684d5 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup(name="python-instagram", - version="0.6.0", + version="0.7.0", description="Instagram API client", license="MIT", install_requires=["simplejson","httplib2"], @@ -12,4 +12,3 @@ packages = find_packages(), keywords= "instagram", zip_safe = True) - From 142c64ed9234668cb6cd52cc613ff2444ab0a56c Mon Sep 17 00:00:00 2001 From: Mike Krieger Date: Fri, 24 Feb 2012 09:50:04 -0800 Subject: [PATCH 39/95] Updating readme with pip instructions --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d196444d..dd17a3de 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A Python client for the Instagram REST and Search APIs Installation ----- -python setup.py install +pip install python-instagram Requires ----- From 38eb254e4e22f3360f24a03b6ab2cdbedf6f3174 Mon Sep 17 00:00:00 2001 From: Mike Helmick Date: Fri, 24 Feb 2012 13:48:25 -0500 Subject: [PATCH 40/95] Default args should not be mutable I can't find the exact blog that I learned it on, but this should be a good enough explaination: http://www.itmaybeahack.com/book/python-2.6/html/p02/p02c05_maps.html#us ing-dictionaries-as-function-parameter-defaults --- instagram/bind.py | 6 ++++-- instagram/oauth2.py | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/instagram/bind.py b/instagram/bind.py index b05b190b..8100b463 100644 --- a/instagram/bind.py +++ b/instagram/bind.py @@ -82,7 +82,8 @@ def _build_path(self): self.path = self.path.replace(variable, value) self.path = self.path + '.%s' % self.api.format - def _do_api_request(self, url, method="GET", body=None, headers={}): + def _do_api_request(self, url, method="GET", body=None, headers=None): + headers = headers or {} response, content = OAuth2Request(self.api).make_request(url, method=method, body=body, headers=headers) if response['status'] == '503': raise InstagramAPIError(response['status'], "Rate limited", "Your client is making too many request per second") @@ -110,7 +111,8 @@ def _do_api_request(self, url, method="GET", body=None, headers={}): else: raise InstagramAPIError(status_code, content_obj['meta']['error_type'], content_obj['meta']['error_message']) - def _paginator_with_url(self, url, method="GET", body=None, headers={}): + def _paginator_with_url(self, url, method="GET", body=None, headers=None): + headers = headers or {} pages_read = 0 while url and pages_read < self.max_pages: api_responses, url = self._do_api_request(url, method, body, headers) diff --git a/instagram/oauth2.py b/instagram/oauth2.py index 1f3cc765..029e2d8a 100644 --- a/instagram/oauth2.py +++ b/instagram/oauth2.py @@ -200,7 +200,8 @@ def prepare_request(self, method, path, params, include_secret=False): return url, method, body, headers - def make_request(self, url, method="GET", body=None, headers={}): + def make_request(self, url, method="GET", body=None, headers=None): + headers = headers or {} if not 'User-Agent' in headers: headers.update({"User-Agent":"%s Python Client" % self.api.api_name}) http_obj = Http(disable_ssl_certificate_validation=True) From d37641dfc8654fdeab04dd2d907df0c6b8a50415 Mon Sep 17 00:00:00 2001 From: Michael Helmick Date: Fri, 24 Feb 2012 17:38:38 -0500 Subject: [PATCH 41/95] Fix #15 --- instagram/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instagram/client.py b/instagram/client.py index 6756968f..60c18e07 100644 --- a/instagram/client.py +++ b/instagram/client.py @@ -57,7 +57,7 @@ def __init__(self, *args, **kwargs): path = "/media/{media_id}/comments", method = "POST", accepts_parameters = ['media_id', 'text'], - response_type = "entry", + response_type = "empty", root_class = Comment) delete_comment = bind_method( From c3fd4f5c4a4e4291e837468e9e9f62ef115b1d43 Mon Sep 17 00:00:00 2001 From: Michael Helmick Date: Fri, 24 Feb 2012 17:38:38 -0500 Subject: [PATCH 42/95] Fix #15 --- instagram/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instagram/client.py b/instagram/client.py index 6756968f..60c18e07 100644 --- a/instagram/client.py +++ b/instagram/client.py @@ -57,7 +57,7 @@ def __init__(self, *args, **kwargs): path = "/media/{media_id}/comments", method = "POST", accepts_parameters = ['media_id', 'text'], - response_type = "entry", + response_type = "empty", root_class = Comment) delete_comment = bind_method( From d8a1d48a65b26d8328b223a7285d089affc0c074 Mon Sep 17 00:00:00 2001 From: Mike Krieger Date: Fri, 24 Feb 2012 16:14:07 -0800 Subject: [PATCH 43/95] Add an 'empty' type explicitly --- instagram/bind.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/instagram/bind.py b/instagram/bind.py index 8100b463..91c3992e 100644 --- a/instagram/bind.py +++ b/instagram/bind.py @@ -16,7 +16,7 @@ def __str__(self): return self.error_message class InstagramAPIError(Exception): - + def __init__(self, status_code, error_type, error_message, *args, **kwargs): self.status_code = status_code self.error_type = error_type @@ -46,7 +46,7 @@ def __init__(self, api, *args, **kwargs): self.max_pages = kwargs.pop("max_pages", 3) self.parameters = {} self._build_parameters(args, kwargs) - self._build_path() + self._build_path() def _build_parameters(self, args, kwargs): # via tweepy https://github.com/joshthecoder/tweepy/ @@ -107,7 +107,9 @@ def _do_api_request(self, url, method="GET", body=None, headers=None): api_responses = data else: api_responses = self.root_class.object_from_dictionary(data) - return api_responses, content_obj.get('pagination', {}).get('next_url') + elif self.response_type == 'empty': + pass + return api_responses, content_obj.get('pagination', {}).get('next_url') else: raise InstagramAPIError(status_code, content_obj['meta']['error_type'], content_obj['meta']['error_message']) @@ -117,13 +119,13 @@ def _paginator_with_url(self, url, method="GET", body=None, headers=None): while url and pages_read < self.max_pages: api_responses, url = self._do_api_request(url, method, body, headers) pages_read += 1 - yield api_responses, url + yield api_responses, url return def execute(self): - url, method, body, headers = OAuth2Request(self.api).prepare_request(self.method, - self.path, - self.parameters, + url, method, body, headers = OAuth2Request(self.api).prepare_request(self.method, + self.path, + self.parameters, include_secret = self.include_secret) if self.as_generator: return self._paginator_with_url(url, method, body, headers) From bcb0f716667daea74d07fb9b3962e8ca99dc625b Mon Sep 17 00:00:00 2001 From: Mike Krieger Date: Fri, 24 Feb 2012 16:18:15 -0800 Subject: [PATCH 44/95] Version bump --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 62d684d5..0ab8b449 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup(name="python-instagram", - version="0.7.0", + version="0.7.1", description="Instagram API client", license="MIT", install_requires=["simplejson","httplib2"], From cb7074687fbeecf388e4e97dc5aa118f37a3148e Mon Sep 17 00:00:00 2001 From: Mike Krieger Date: Fri, 24 Feb 2012 16:18:25 -0800 Subject: [PATCH 45/95] Adding tags; fixes #6 --- instagram/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/instagram/models.py b/instagram/models.py index 9e6fd8aa..146bca8c 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -60,6 +60,8 @@ def object_from_dictionary(cls, entry): if entry['caption']: new_media.caption = Comment.object_from_dictionary(entry['caption']) + new_media.tags = entry['tags'] + new_media.link = entry['link'] new_media.filter = entry.get('filter') From 67a60c69e875a39eff0fec1d8698ed22df274e5a Mon Sep 17 00:00:00 2001 From: Mike Krieger Date: Fri, 24 Feb 2012 16:18:39 -0800 Subject: [PATCH 46/95] Another version bump --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0ab8b449..add923aa 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup(name="python-instagram", - version="0.7.1", + version="0.7.2", description="Instagram API client", license="MIT", install_requires=["simplejson","httplib2"], From 9132cef5688dcd600a5f0eeaf2e71682b60b73db Mon Sep 17 00:00:00 2001 From: Mike Krieger Date: Fri, 24 Feb 2012 16:22:05 -0800 Subject: [PATCH 47/95] Readme updates --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index dd17a3de..5f023475 100644 --- a/README.md +++ b/README.md @@ -11,10 +11,12 @@ Requires * httplib2 * simplejson -Follow @instagramapi on Twitter ----------------------------- -You can [follow @instagramapi on Twitter](http://twitter.com/#!/instagramapi) for announcements, -updates, and news about the Instagram gem. + +Discussion +------ + +Visit [our Google Group](http://groups.google.com/group/instagram-api-developers) to discuss the Instagram API. + Obtaining an access token ----- @@ -40,4 +42,3 @@ We also provide a one-file sample app using bottle (you'll have to 'pip install * Open up sample\_app.py, update it with your client\_id and secret, and set redirect URI to 'http://localhost:8515/oauth_callback' * Run the file; it will host a local server on port 8515. * Try visiting http://localhost:8515 in your browser - From 6b43209b69afb85552d29df8eb1ba37278d8e8f6 Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Tue, 28 Feb 2012 11:32:48 +0000 Subject: [PATCH 48/95] Make it easier to identify various models with a __unicode__ method. When displaying, we'll get more useful information that the name of the object and it's memory location that way. --- instagram/models.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/instagram/models.py b/instagram/models.py index 146bca8c..36a506e3 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -15,6 +15,9 @@ def __init__(self, url, width, height): self.height = height self.width = width + def __unicode__(self): + return "Image: %s" % self.url + class Media(ApiModel): def __init__(self, id=None, **kwargs): @@ -25,6 +28,9 @@ def __init__(self, id=None, **kwargs): def get_standard_resolution_url(self): return self.images['standard_resolution'].url + def __unicode__(self): + return "Media: %s" % self.id + @classmethod def object_from_dictionary(cls, entry): new_media = Media(id=entry['id']) @@ -101,6 +107,9 @@ def __init__(self, latitude, longitude): self.latitude = latitude self.longitude = longitude + def __unicode__(self): + return "Point: (%s, %s)" % (self.latitude, self.longitude) + class Location(ApiModel): def __init__(self, id, *args, **kwargs): self.id = id @@ -118,6 +127,9 @@ def object_from_dictionary(cls, entry): name=entry.get('name', '')) return location + def __unicode__(self): + return "Location: %s (%s)" % (self.id, self.point) + class User(ApiModel): def __init__(self, id, *args, **kwargs): From 17cb064ca81ae2a5ee15f7f04ca009051fa5974f Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Tue, 28 Feb 2012 11:37:48 +0000 Subject: [PATCH 49/95] Switch __str__ to __unicode__ Let's be consistent in which method models use to identify themselves. --- instagram/models.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/instagram/models.py b/instagram/models.py index 36a506e3..65a9c40e 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -8,6 +8,9 @@ def object_from_dictionary(cls, entry): entry_str_dict = dict([(str(key), value) for key,value in entry.items()]) return cls(**entry_str_dict) + def __repr__(self): + return unicode(self).encode('utf8') + class Image(ApiModel): def __init__(self, url, width, height): @@ -80,8 +83,8 @@ def __init__(self, name, **kwargs): for key,value in kwargs.iteritems(): setattr(self, key, value) - def __str__(self): - return "Tag %s" % self.name + def __unicode__(self): + return "Tag: %s" % self.name class Comment(ApiModel): def __init__(self, *args, **kwargs): @@ -137,8 +140,8 @@ def __init__(self, id, *args, **kwargs): for key,value in kwargs.iteritems(): setattr(self, key, value) - def __str__(self): - return "User %s" % self.username + def __unicode__(self): + return "User: %s" % self.username class Relationship(ApiModel): From 6c2d83b0332dcbb84cf3f8b0c698db85dc92b314 Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Tue, 28 Feb 2012 11:42:15 +0000 Subject: [PATCH 50/95] Prefix this with what kind of object it is. --- instagram/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instagram/models.py b/instagram/models.py index 65a9c40e..afa91d2f 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -100,7 +100,7 @@ def object_from_dictionary(cls, entry): return cls(id=id, user=user, text=text, created_at=created_at) def __unicode__(self): - return "%s said \"%s\"" % (self.user.username, self.message) + return "Comment: %s said \"%s\"" % (self.user.username, self.text) class Caption(Comment): pass From c4a4415a986acc1212356fb2a241115bf962f61c Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Tue, 28 Feb 2012 11:52:45 +0000 Subject: [PATCH 51/95] Remove duplicated settings of the caption. A Caption object is identical to a Comment object. --- instagram/models.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/instagram/models.py b/instagram/models.py index 146bca8c..d1650152 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -47,10 +47,6 @@ def object_from_dictionary(cls, entry): for comment in entry['comments']['data']: new_media.comments.append(Comment.object_from_dictionary(comment)) - new_media.caption = None - if entry['caption']: - new_media.caption = Caption.object_from_dictionary(entry['caption']) - new_media.created_time = timestamp_to_datetime(entry['created_time']) if entry['location'] and entry.has_key('id'): From e079e1a594dd43475532c436769b893363e3a492 Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Tue, 28 Feb 2012 11:55:16 +0000 Subject: [PATCH 52/95] There are no users of the Caption model, remove it. Unclear if it was introduced by error or deliberately. But it seems unnecessary. --- instagram/models.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/instagram/models.py b/instagram/models.py index d1650152..f6a0d31c 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -89,9 +89,6 @@ def object_from_dictionary(cls, entry): def __unicode__(self): return "%s said \"%s\"" % (self.user.username, self.message) -class Caption(Comment): - pass - class Point(ApiModel): def __init__(self, latitude, longitude): self.latitude = latitude @@ -130,5 +127,3 @@ def __init__(self, incoming_status="none", outgoing_status="none", target_user_i self.incoming_status = incoming_status self.outgoing_status = outgoing_status self.target_user_is_private = target_user_is_private - - From 727939eeed0f4563f5b21b56f449f6b9c2163d1a Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Tue, 28 Feb 2012 11:57:44 +0000 Subject: [PATCH 53/95] Explicitly construct the appropriate object. object_from_dictionary is used to put together the object for us. So, when we override, explicitly instantiate the right model class. --- instagram/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/instagram/models.py b/instagram/models.py index afa91d2f..72fc2dac 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -97,7 +97,7 @@ def object_from_dictionary(cls, entry): text = entry['text'] created_at = timestamp_to_datetime(entry['created_time']) id = entry['id'] - return cls(id=id, user=user, text=text, created_at=created_at) + return Comment(id=id, user=user, text=text, created_at=created_at) def __unicode__(self): return "Comment: %s said \"%s\"" % (self.user.username, self.text) @@ -125,7 +125,7 @@ def object_from_dictionary(cls, entry): if 'latitude' in entry: point = Point(entry.get('latitude'), entry.get('longitude')) - location = cls(entry.get('id', 0), + location = Location(entry.get('id', 0), point=point, name=entry.get('name', '')) return location From 9b8d2af4cc78be492d4ef12b56cc68deef6133f0 Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Tue, 28 Feb 2012 12:19:12 +0000 Subject: [PATCH 54/95] Convert the Tags dictionary into Tags objects too. --- instagram/models.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/instagram/models.py b/instagram/models.py index 146bca8c..fb8dff8a 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -61,6 +61,10 @@ def object_from_dictionary(cls, entry): new_media.caption = Comment.object_from_dictionary(entry['caption']) new_media.tags = entry['tags'] + if entry['tags']: + new_media.tags = [] + for tag in entry['tags']: + new_media.tags.append(Tag.object_from_dictionary({'name': tag})) new_media.link = entry['link'] From 0deeabb722acea09cfa7f068a89d1f6099e4440b Mon Sep 17 00:00:00 2001 From: Michael Helmick Date: Wed, 29 Feb 2012 11:22:53 -0500 Subject: [PATCH 55/95] Datetime to Timestamp helper --- instagram/helper.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/instagram/helper.py b/instagram/helper.py index 54de7ecf..62bcf5b5 100644 --- a/instagram/helper.py +++ b/instagram/helper.py @@ -1,4 +1,10 @@ +import calendar from datetime import datetime + def timestamp_to_datetime(ts): return datetime.utcfromtimestamp(float(ts)) + + +def datetime_to_timestamp(dt): + return calendar.timegm(dt.timetuple()) From 911e4c93cf0d95b8f57fe8288873fd34cf9bf2bc Mon Sep 17 00:00:00 2001 From: Michael Helmick Date: Wed, 29 Feb 2012 11:33:20 -0500 Subject: [PATCH 56/95] PEP8 Cleanup Not sure if you guys really care, but if ya do.. here ya go. :) --- instagram/bind.py | 7 +- instagram/client.py | 230 ++++++++++++++++++------------------- instagram/models.py | 27 +++-- instagram/oauth2.py | 39 ++++--- instagram/subscriptions.py | 8 +- 5 files changed, 163 insertions(+), 148 deletions(-) diff --git a/instagram/bind.py b/instagram/bind.py index 91c3992e..6e7db910 100644 --- a/instagram/bind.py +++ b/instagram/bind.py @@ -4,10 +4,12 @@ import simplejson re_path_template = re.compile('{\w+}') + def encode_string(value): return value.encode('utf-8') \ if isinstance(value, unicode) else str(value) + class InstagramClientError(Exception): def __init__(self, error_message): self.error_message = error_message @@ -15,6 +17,7 @@ def __init__(self, error_message): def __str__(self): return self.error_message + class InstagramAPIError(Exception): def __init__(self, status_code, error_type, error_message, *args, **kwargs): @@ -25,6 +28,7 @@ def __init__(self, status_code, error_type, error_message, *args, **kwargs): def __str__(self): return "(%s) %s-%s" % (self.status_code, self.error_type, self.error_message) + def bind_method(**config): class InstagramAPIMethod(object): @@ -126,7 +130,7 @@ def execute(self): url, method, body, headers = OAuth2Request(self.api).prepare_request(self.method, self.path, self.parameters, - include_secret = self.include_secret) + include_secret=self.include_secret) if self.as_generator: return self._paginator_with_url(url, method, body, headers) else: @@ -136,7 +140,6 @@ def execute(self): else: return content - def _call(api, *args, **kwargs): method = InstagramAPIMethod(api, *args, **kwargs) return method.execute() diff --git a/instagram/client.py b/instagram/client.py index 60c18e07..07958349 100644 --- a/instagram/client.py +++ b/instagram/client.py @@ -7,8 +7,9 @@ SUPPORTED_FORMATS = ['json'] + class InstagramAPI(oauth2.OAuth2API): - + host = "api.instagram.com" base_path = "/v1" access_token_field = "access_token" @@ -25,154 +26,153 @@ def __init__(self, *args, **kwargs): raise Exception("Unsupported format") super(InstagramAPI, self).__init__(*args, **kwargs) - media_popular = bind_method( - path = "/media/popular", - accepts_parameters = MEDIA_ACCEPT_PARAMETERS, - root_class = Media) + path="/media/popular", + accepts_parameters=MEDIA_ACCEPT_PARAMETERS, + root_class=Media) media_search = bind_method( - path = "/media/search", - accepts_parameters = SEARCH_ACCEPT_PARAMETERS + ['lat', 'lng', 'min_timestamp', 'max_timestamp'], - root_class = Media) - + path="/media/search", + accepts_parameters=SEARCH_ACCEPT_PARAMETERS + ['lat', 'lng', 'min_timestamp', 'max_timestamp'], + root_class=Media) + media_likes = bind_method( - path = "/media/{media_id}/likes", - accepts_parameters = ['media_id'], - root_class = User) + path="/media/{media_id}/likes", + accepts_parameters=['media_id'], + root_class=User) like_media = bind_method( - path = "/media/{media_id}/likes", - method = "POST", - accepts_parameters = ['media_id'], - response_type = "empty") + path="/media/{media_id}/likes", + method="POST", + accepts_parameters=['media_id'], + response_type="empty") unlike_media = bind_method( - path = "/media/{media_id}/likes", - method = "DELETE", - accepts_parameters = ['media_id'], - response_type = "empty") + path="/media/{media_id}/likes", + method="DELETE", + accepts_parameters=['media_id'], + response_type="empty") create_media_comment = bind_method( - path = "/media/{media_id}/comments", - method = "POST", - accepts_parameters = ['media_id', 'text'], - response_type = "empty", - root_class = Comment) + path="/media/{media_id}/comments", + method="POST", + accepts_parameters=['media_id', 'text'], + response_type="empty", + root_class=Comment) delete_comment = bind_method( - path = "/media/{media_id}/comments/{comment_id}", - method = "DELETE", - accepts_parameters = ['media_id', 'comment_id'], - response_type = "empty") + path="/media/{media_id}/comments/{comment_id}", + method="DELETE", + accepts_parameters=['media_id', 'comment_id'], + response_type="empty") media_comments = bind_method( - path = "/media/{media_id}/comments", - method = "GET", - accepts_parameters = ['media_id'], - response_type = "list", - root_class = Comment) + path="/media/{media_id}/comments", + method="GET", + accepts_parameters=['media_id'], + response_type="list", + root_class=Comment) media = bind_method( - path = "/media/{media_id}", - accepts_parameters = ['media_id'], - response_type = "entry", - root_class = Media) + path="/media/{media_id}", + accepts_parameters=['media_id'], + response_type="entry", + root_class=Media) user_media_feed = bind_method( - path = "/users/self/feed", - accepts_parameters = MEDIA_ACCEPT_PARAMETERS, - root_class = Media, - paginates = True) + path="/users/self/feed", + accepts_parameters=MEDIA_ACCEPT_PARAMETERS, + root_class=Media, + paginates=True) user_liked_media = bind_method( - path = "/users/self/media/liked", - accepts_parameters = MEDIA_ACCEPT_PARAMETERS, - root_class = Media, - paginates = True) + path="/users/self/media/liked", + accepts_parameters=MEDIA_ACCEPT_PARAMETERS, + root_class=Media, + paginates=True) user_recent_media = bind_method( - path = "/users/{user_id}/media/recent", - accepts_parameters = MEDIA_ACCEPT_PARAMETERS + ['user_id'], - root_class = Media, - paginates = True) + path="/users/{user_id}/media/recent", + accepts_parameters=MEDIA_ACCEPT_PARAMETERS + ['user_id'], + root_class=Media, + paginates=True) user_search = bind_method( - path = "/users/search", - accepts_parameters = SEARCH_ACCEPT_PARAMETERS, - root_class = User) + path="/users/search", + accepts_parameters=SEARCH_ACCEPT_PARAMETERS, + root_class=User) user_follows = bind_method( - path = "/users/{user_id}/follows", - accepts_parameters = ["user_id"], - paginates = True, - root_class = User) + path="/users/{user_id}/follows", + accepts_parameters=["user_id"], + paginates=True, + root_class=User) user_followed_by = bind_method( - path = "/users/{user_id}/followed-by", - accepts_parameters = ["user_id"], - paginates = True, - root_class = User) + path="/users/{user_id}/followed-by", + accepts_parameters=["user_id"], + paginates=True, + root_class=User) user = bind_method( - path = "/users/{user_id}", - accepts_parameters = ["user_id"], - root_class = User, - response_type = "entry") - + path="/users/{user_id}", + accepts_parameters=["user_id"], + root_class=User, + response_type="entry") + location_recent_media = bind_method( - path = "/locations/{location_id}/media/recent", - accepts_parameters = MEDIA_ACCEPT_PARAMETERS + ['location_id'], - root_class = Media, - paginates = True) + path="/locations/{location_id}/media/recent", + accepts_parameters=MEDIA_ACCEPT_PARAMETERS + ['location_id'], + root_class=Media, + paginates=True) location_search = bind_method( - path = "/locations/search", - accepts_parameters = SEARCH_ACCEPT_PARAMETERS + ['lat', 'lng', 'foursquare_id'], - root_class = Location) + path="/locations/search", + accepts_parameters=SEARCH_ACCEPT_PARAMETERS + ['lat', 'lng', 'foursquare_id'], + root_class=Location) location = bind_method( - path = "/locations/{location_id}", - accepts_parameters = ["location_id"], - root_class = Location, - response_type = "entry") + path="/locations/{location_id}", + accepts_parameters=["location_id"], + root_class=Location, + response_type="entry") geography_recent_media = bind_method( - path = "/geographies/{geography_id}/media/recent", - accepts_parameters = MEDIA_ACCEPT_PARAMETERS + ["geography_id"], - root_class = Media, - paginates = True) + path="/geographies/{geography_id}/media/recent", + accepts_parameters=MEDIA_ACCEPT_PARAMETERS + ["geography_id"], + root_class=Media, + paginates=True) tag_recent_media = bind_method( - path = "/tags/{tag_name}/media/recent", - accepts_parameters = MEDIA_ACCEPT_PARAMETERS + ['tag_name'], - root_class = Media, - paginates = True) + path="/tags/{tag_name}/media/recent", + accepts_parameters=MEDIA_ACCEPT_PARAMETERS + ['tag_name'], + root_class=Media, + paginates=True) tag_search = bind_method( - path = "/tags/search", - accepts_parameters = SEARCH_ACCEPT_PARAMETERS, - root_class = Tag, - paginates = True) + path="/tags/search", + accepts_parameters=SEARCH_ACCEPT_PARAMETERS, + root_class=Tag, + paginates=True) tag = bind_method( - path = "/tags/{tag_name}", - accepts_parameters = ["tag_name"], - root_class = Tag, - response_type = "entry") + path="/tags/{tag_name}", + accepts_parameters=["tag_name"], + root_class=Tag, + response_type="entry") user_incoming_requests = bind_method( - path = "/users/self/requested-by", - root_class = User) + path="/users/self/requested-by", + root_class=User) change_user_relationship = bind_method( - method = "POST", - path = "/users/{user_id}/relationship", - root_class = Relationship, - accepts_parameters = ["user_id", "action"], - paginates = True, - requires_target_user = True, - response_type = "entry") + method="POST", + path="/users/{user_id}/relationship", + root_class=Relationship, + accepts_parameters=["user_id", "action"], + paginates=True, + requires_target_user=True, + response_type="entry") def _make_relationship_shortcut(action): def _inner(self, *args, **kwargs): @@ -187,16 +187,14 @@ def _inner(self, *args, **kwargs): approve_user_request = _make_relationship_shortcut('approve') ignore_user_request = _make_relationship_shortcut('ignore') - - def _make_subscription_action(method, include=None, exclude=None): - accepts_parameters = ["object", - "aspect", - "object_id", # Optional if subscribing to all users - "callback_url", - "lat", # Geography - "lng", # Geography - "radius", # Geography + accepts_parameters = ["object", + "aspect", + "object_id", # Optional if subscribing to all users + "callback_url", + "lat", # Geography + "lng", # Geography + "radius", # Geography "verify_token"] if include: @@ -204,11 +202,11 @@ def _make_subscription_action(method, include=None, exclude=None): if exclude: accepts_parameters = [x for x in accepts_parameters if x not in exclude] return bind_method( - path = "/subscriptions", - method = method, - accepts_parameters = accepts_parameters, - include_secret = True, - objectify_response = False + path="/subscriptions", + method=method, + accepts_parameters=accepts_parameters, + include_secret=True, + objectify_response=False ) create_subscription = _make_subscription_action('POST') diff --git a/instagram/models.py b/instagram/models.py index f2ace60a..e5c29a93 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -1,16 +1,18 @@ from helper import timestamp_to_datetime + class ApiModel(object): @classmethod def object_from_dictionary(cls, entry): # make dict keys all strings - entry_str_dict = dict([(str(key), value) for key,value in entry.items()]) + entry_str_dict = dict([(str(key), value) for key, value in entry.items()]) return cls(**entry_str_dict) def __repr__(self): return unicode(self).encode('utf8') + class Image(ApiModel): def __init__(self, url, width, height): @@ -21,11 +23,12 @@ def __init__(self, url, width, height): def __unicode__(self): return "Image: %s" % self.url + class Media(ApiModel): def __init__(self, id=None, **kwargs): self.id = id - for key,value in kwargs.iteritems(): + for key, value in kwargs.iteritems(): setattr(self, key, value) def get_standard_resolution_url(self): @@ -40,14 +43,14 @@ def object_from_dictionary(cls, entry): new_media.user = User.object_from_dictionary(entry['user']) new_media.images = {} - for version,version_info in entry['images'].iteritems(): + for version, version_info in entry['images'].iteritems(): new_media.images[version] = Image.object_from_dictionary(version_info) if 'user_has_liked' in entry: new_media.user_has_liked = entry['user_has_liked'] new_media.like_count = entry['likes']['count'] new_media.likes = [] - if entry['likes'].has_key('data'): + if 'data' in entry['likes']: for like in entry['likes']['data']: new_media.likes.append(User.object_from_dictionary(like)) @@ -58,7 +61,7 @@ def object_from_dictionary(cls, entry): new_media.created_time = timestamp_to_datetime(entry['created_time']) - if entry['location'] and entry.has_key('id'): + if entry['location'] and 'id' in entry: new_media.location = Location.object_from_dictionary(entry['location']) new_media.caption = None @@ -77,18 +80,20 @@ def object_from_dictionary(cls, entry): return new_media + class Tag(ApiModel): def __init__(self, name, **kwargs): self.name = name - for key,value in kwargs.iteritems(): + for key, value in kwargs.iteritems(): setattr(self, key, value) def __unicode__(self): return "Tag: %s" % self.name + class Comment(ApiModel): def __init__(self, *args, **kwargs): - for key,value in kwargs.iteritems(): + for key, value in kwargs.iteritems(): setattr(self, key, value) @classmethod @@ -102,6 +107,7 @@ def object_from_dictionary(cls, entry): def __unicode__(self): return "Comment: %s said \"%s\"" % (self.user.username, self.text) + class Point(ApiModel): def __init__(self, latitude, longitude): self.latitude = latitude @@ -110,10 +116,11 @@ def __init__(self, latitude, longitude): def __unicode__(self): return "Point: (%s, %s)" % (self.latitude, self.longitude) + class Location(ApiModel): def __init__(self, id, *args, **kwargs): self.id = id - for key,value in kwargs.iteritems(): + for key, value in kwargs.iteritems(): setattr(self, key, value) @classmethod @@ -130,16 +137,18 @@ def object_from_dictionary(cls, entry): def __unicode__(self): return "Location: %s (%s)" % (self.id, self.point) + class User(ApiModel): def __init__(self, id, *args, **kwargs): self.id = id - for key,value in kwargs.iteritems(): + for key, value in kwargs.iteritems(): setattr(self, key, value) def __unicode__(self): return "User: %s" % self.username + class Relationship(ApiModel): def __init__(self, incoming_status="none", outgoing_status="none", target_user_is_private=False): diff --git a/instagram/oauth2.py b/instagram/oauth2.py index 029e2d8a..d54a7393 100644 --- a/instagram/oauth2.py +++ b/instagram/oauth2.py @@ -3,6 +3,7 @@ from httplib2 import Http import mimetypes + class OAuth2AuthExchangeError(Exception): def __init__(self, description): self.description = description @@ -10,6 +11,7 @@ def __init__(self, description): def __str__(self): return self.description + class OAuth2API(object): host = None base_path = None @@ -30,26 +32,27 @@ def __init__(self, client_id=None, client_secret=None, access_token=None, redire def get_authorize_url(self, scope=None): req = OAuth2AuthExchangeRequest(self) - return req.get_authorize_url(scope = scope) + return req.get_authorize_url(scope=scope) def get_authorize_login_url(self, scope=None): """ scope should be a tuple or list of requested scope access levels """ req = OAuth2AuthExchangeRequest(self) - return req.get_authorize_login_url(scope = scope) + return req.get_authorize_login_url(scope=scope) def exchange_code_for_access_token(self, code): req = OAuth2AuthExchangeRequest(self) - return req.exchange_for_access_token(code = code) + return req.exchange_for_access_token(code=code) def exchange_user_id_for_access_token(self, user_id): req = OAuth2AuthExchangeRequest(self) - return req.exchange_for_access_token(user_id = user_id) + return req.exchange_for_access_token(user_id=user_id) def exchange_xauth_login_for_access_token(self, username, password, scope=None): """ scope should be a tuple or list of requested scope access levels """ req = OAuth2AuthExchangeRequest(self) - return req.exchange_for_access_token(username = username, password = password, - scope = scope) + return req.exchange_for_access_token(username=username, password=password, + scope=scope) + class OAuth2AuthExchangeRequest(object): def __init__(self, api): @@ -62,7 +65,7 @@ def _url_for_authorize(self, scope=None): "redirect_uri": self.api.redirect_uri } if scope: - client_params.update(scope = ' '.join(scope)) + client_params.update(scope=' '.join(scope)) url_params = urllib.urlencode(client_params) return "%s?%s" % (self.api.authorize_url, url_params) @@ -76,22 +79,22 @@ def _data_for_exchange(self, code=None, username=None, password=None, scope=None if code: client_params.update(code=code) elif username and password: - client_params.update(username = username, - password = password, - grant_type = "password") + client_params.update(username=username, + password=password, + grant_type="password") if scope: - client_params.update(scope = ' '.join(scope)) + client_params.update(scope=' '.join(scope)) elif user_id: - client_params.update(user_id = user_id) + client_params.update(user_id=user_id) return urllib.urlencode(client_params) def get_authorize_url(self, scope=None): - return self._url_for_authorize(scope = scope) + return self._url_for_authorize(scope=scope) def get_authorize_login_url(self, scope=None): http_object = Http(disable_ssl_certificate_validation=True) - url = self._url_for_authorize(scope = scope) + url = self._url_for_authorize(scope=scope) response, content = http_object.request(url) if response['status'] != '200': raise OAuth2AuthExchangeError("The server returned a non-200 response for URL %s" % url) @@ -99,7 +102,7 @@ def get_authorize_login_url(self, scope=None): return redirected_to def exchange_for_access_token(self, code=None, username=None, password=None, scope=None, user_id=None): - data = self._data_for_exchange(code, username, password, scope = scope, user_id = user_id) + data = self._data_for_exchange(code, username, password, scope=scope, user_id=user_id) http_object = Http(disable_ssl_certificate_validation=True) url = self.api.access_token_url response, content = http_object.request(url, method="POST", body=data) @@ -172,7 +175,7 @@ def encode_file(field_name): for field in files: lines.extend(encode_file(field)) lines.extend(("--%s--" % (boundary), "")) - body = "\r\n".join (lines) + body = "\r\n".join(lines) headers = {"Content-Type": "multipart/form-data; boundary=" + boundary, "Content-Length": str(len(body))} @@ -195,7 +198,7 @@ def prepare_request(self, method, path, params, include_secret=False): else: url = self._full_url_with_params(path, params, include_secret) else: - body, headers = encode_multipart(params, params['files']) + body, headers = self._encode_multipart(params, params['files']) url = self._full_url(path) return url, method, body, headers @@ -203,6 +206,6 @@ def prepare_request(self, method, path, params, include_secret=False): def make_request(self, url, method="GET", body=None, headers=None): headers = headers or {} if not 'User-Agent' in headers: - headers.update({"User-Agent":"%s Python Client" % self.api.api_name}) + headers.update({"User-Agent": "%s Python Client" % self.api.api_name}) http_obj = Http(disable_ssl_certificate_validation=True) return http_obj.request(url, method, body=body, headers=headers) diff --git a/instagram/subscriptions.py b/instagram/subscriptions.py index 24cf6676..1a946a05 100644 --- a/instagram/subscriptions.py +++ b/instagram/subscriptions.py @@ -2,15 +2,18 @@ import hashlib import simplejson + class SubscriptionType: TAG = 'tag' USER = 'user' GEOGRAPHY = 'geography' LOCATION = 'location' + class SubscriptionVerifyError(Exception): pass + class SubscriptionsReactor(object): callbacks = {} @@ -41,9 +44,8 @@ def deregister_callback(self, object_type, callback): callbacks.remove(callback) def _verify_signature(self, client_secret, raw_response, x_hub_signature): - digest = hmac.new(client_secret.encode('utf-8'), - msg=raw_response.encode('utf-8'), + digest = hmac.new(client_secret.encode('utf-8'), + msg=raw_response.encode('utf-8'), digestmod=hashlib.sha1 ).hexdigest() return digest == x_hub_signature - From 25780e1839a55759a7dad525949247222bd3241a Mon Sep 17 00:00:00 2001 From: Mike Krieger Date: Fri, 2 Mar 2012 11:44:13 -0800 Subject: [PATCH 57/95] Updating fixture; temp removing test that doesn't match what we do in the API --- fixtures/media_popular.json | 5844 ++++++++++++++++--------------- fixtures/media_search.json | 696 ++-- fixtures/tag_recent_media.json | 207 +- fixtures/user_liked_media.json | 87 +- fixtures/user_media_feed.json | 85 +- fixtures/user_recent_media.json | 83 +- instagram/models.py | 1 - tests.py | 4 + 8 files changed, 3528 insertions(+), 3479 deletions(-) diff --git a/fixtures/media_popular.json b/fixtures/media_popular.json index 91c4a750..ec252a51 100644 --- a/fixtures/media_popular.json +++ b/fixtures/media_popular.json @@ -1,4615 +1,4647 @@ { "meta": { "code": 200 - }, + }, "data": [ { - "type": "image", - "location": null, + "type": "image", + "location": null, + "tags": [], "comments": { - "count": 1, + "count": 1, "data": [ { - "created_time": "1289324628", - "text": "Mama Bird......", + "created_time": "1289324628", + "text": "Mama Bird......", "from": { - "username": "gigi22", - "first_name": "GIna", - "last_name": "G", - "type": "user", + "username": "gigi22", + "first_name": "GIna", + "last_name": "G", + "type": "user", "id": "31123" - }, + }, "id": "2607969" } ] - }, + }, "caption": { - "created_time": "1289324628", - "text": "Mama Bird......", + "created_time": "1289324628", + "text": "Mama Bird......", "from": { - "username": "gigi22", - "first_name": "GIna", - "last_name": "G", - "type": "user", + "username": "gigi22", + "first_name": "GIna", + "last_name": "G", + "type": "user", "id": "31123" - }, + }, "id": "2607969" - }, - "link": "http://localhost:8000/p/M5Do/", + }, + "link": "http://localhost:8000/p/M5Do/", "likes": { "count": 12 - }, - "created_time": "1289324623", + }, + "created_time": "1289324623", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/7ddfcdc916424489b51a877fa7b953bb_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/7ddfcdc916424489b51a877fa7b953bb_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/7ddfcdc916424489b51a877fa7b953bb_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/7ddfcdc916424489b51a877fa7b953bb_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/7ddfcdc916424489b51a877fa7b953bb_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/7ddfcdc916424489b51a877fa7b953bb_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3379432", + }, + "user_has_liked": false, + "id": "3379432", "user": { - "username": "gigi22", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_31123_75sq_1287016111.jpg", + "username": "gigi22", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_31123_75sq_1287016111.jpg", "id": "31123" } - }, + }, { - "type": "image", - "location": null, + "type": "image", + "location": null, + "tags": [], "comments": { - "count": 4, + "count": 4, "data": [ { - "created_time": "1289319725", - "text": "Central Park, NYC", + "created_time": "1289319725", + "text": "Central Park, NYC", "from": { - "username": "juanpas9", - "first_name": "Juan", - "last_name": "Pascual", - "type": "user", + "username": "juanpas9", + "first_name": "Juan", + "last_name": "Pascual", + "type": "user", "id": "74932" - }, + }, "id": "2600048" - }, + }, { - "created_time": "1289319764", - "text": "Awesome!!", + "created_time": "1289319764", + "text": "Awesome!!", "from": { - "username": "gershwin", - "first_name": "Gersh", - "last_name": "Win", - "type": "user", + "username": "gershwin", + "first_name": "Gersh", + "last_name": "Win", + "type": "user", "id": "312872" - }, + }, "id": "2600110" - }, + }, { - "created_time": "1289319894", - "text": "nice;)", + "created_time": "1289319894", + "text": "nice;)", "from": { - "username": "3sz", - "first_name": "w", - "last_name": "3suz", - "type": "user", + "username": "3sz", + "first_name": "w", + "last_name": "3suz", + "type": "user", "id": "139346" - }, + }, "id": "2600342" - }, + }, { - "created_time": "1289320097", - "text": "Desde el museo de historia natural", + "created_time": "1289320097", + "text": "Desde el museo de historia natural", "from": { - "username": "juanpas9", - "first_name": "Juan", - "last_name": "Pascual", - "type": "user", + "username": "juanpas9", + "first_name": "Juan", + "last_name": "Pascual", + "type": "user", "id": "74932" - }, + }, "id": "2600724" } ] - }, + }, "caption": { - "created_time": "1289319725", - "text": "Central Park, NYC", + "created_time": "1289319725", + "text": "Central Park, NYC", "from": { - "username": "juanpas9", - "first_name": "Juan", - "last_name": "Pascual", - "type": "user", + "username": "juanpas9", + "first_name": "Juan", + "last_name": "Pascual", + "type": "user", "id": "74932" - }, + }, "id": "2600048" - }, - "link": "http://localhost:8000/p/M3Fp/", + }, + "link": "http://localhost:8000/p/M3Fp/", "likes": { "count": 44 - }, - "created_time": "1289319710", + }, + "created_time": "1289319710", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/27cdce40853b4a2890842d7794de8b52_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/27cdce40853b4a2890842d7794de8b52_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/27cdce40853b4a2890842d7794de8b52_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/27cdce40853b4a2890842d7794de8b52_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/27cdce40853b4a2890842d7794de8b52_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/27cdce40853b4a2890842d7794de8b52_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3371369", + }, + "user_has_liked": false, + "id": "3371369", "user": { - "username": "juanpas9", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_74932_75sq_1286780305.jpg", + "username": "juanpas9", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_74932_75sq_1286780305.jpg", "id": "74932" } - }, + }, { - "type": "image", + "type": "image", + "tags": [], "location": { - "latitude": 31.207768000000002, - "id": "244119", - "longitude": 121.4693609, + "latitude": 31.207768000000002, + "id": "244119", + "longitude": 121.4693609, "name": "田子坊" - }, + }, "comments": { - "count": 1, + "count": 1, "data": [ { - "created_time": "1289269561", - "text": "Tianzifang, Shanghai", + "created_time": "1289269561", + "text": "Tianzifang, Shanghai", "from": { - "username": "jambablast", - "first_name": "Zamba", - "last_name": "", - "type": "user", + "username": "jambablast", + "first_name": "Zamba", + "last_name": "", + "type": "user", "id": "530469" - }, + }, "id": "2512746" } ] - }, + }, "caption": { - "created_time": "1289269561", - "text": "Tianzifang, Shanghai", + "created_time": "1289269561", + "text": "Tianzifang, Shanghai", "from": { - "username": "jambablast", - "first_name": "Zamba", - "last_name": "", - "type": "user", + "username": "jambablast", + "first_name": "Zamba", + "last_name": "", + "type": "user", "id": "530469" - }, + }, "id": "2512746" - }, - "link": "http://localhost:8000/p/MhJV/", + }, + "link": "http://localhost:8000/p/MhJV/", "likes": { "count": 49 - }, - "created_time": "1289269557", + }, + "created_time": "1289269557", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/fa581ac970584f1180dc8e1f4d1f853b_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/fa581ac970584f1180dc8e1f4d1f853b_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/fa581ac970584f1180dc8e1f4d1f853b_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/fa581ac970584f1180dc8e1f4d1f853b_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/fa581ac970584f1180dc8e1f4d1f853b_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/fa581ac970584f1180dc8e1f4d1f853b_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3281493", + }, + "user_has_liked": false, + "id": "3281493", "user": { - "username": "jambablast", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_530469_75sq_1289304798.jpg", + "username": "jambablast", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_530469_75sq_1289304798.jpg", "id": "530469" } - }, + }, { - "type": "image", - "location": null, + "type": "image", + "tags": [], + "location": null, "comments": { - "count": 1, + "count": 1, "data": [ { - "created_time": "1289324688", - "text": "Big drain pipe", + "created_time": "1289324688", + "text": "Big drain pipe", "from": { - "username": "drewmac", - "first_name": "Andrew", - "last_name": "Mackey", - "type": "user", + "username": "drewmac", + "first_name": "Andrew", + "last_name": "Mackey", + "type": "user", "id": "221116" - }, + }, "id": "2608070" } ] - }, + }, "caption": { - "created_time": "1289324688", - "text": "Big drain pipe", + "created_time": "1289324688", + "text": "Big drain pipe", "from": { - "username": "drewmac", - "first_name": "Andrew", - "last_name": "Mackey", - "type": "user", + "username": "drewmac", + "first_name": "Andrew", + "last_name": "Mackey", + "type": "user", "id": "221116" - }, + }, "id": "2608070" - }, - "link": "http://localhost:8000/p/M5FY/", + }, + "link": "http://localhost:8000/p/M5FY/", "likes": { "count": 8 - }, - "created_time": "1289324684", + }, + "created_time": "1289324684", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/4a80b88a2ac4488dbbf6a1a4a85fa6fc_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/4a80b88a2ac4488dbbf6a1a4a85fa6fc_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/4a80b88a2ac4488dbbf6a1a4a85fa6fc_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/4a80b88a2ac4488dbbf6a1a4a85fa6fc_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/4a80b88a2ac4488dbbf6a1a4a85fa6fc_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/4a80b88a2ac4488dbbf6a1a4a85fa6fc_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3379544", + }, + "user_has_liked": false, + "id": "3379544", "user": { - "username": "drewmac", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_221116_75sq_1289323501.jpg", + "username": "drewmac", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_221116_75sq_1289323501.jpg", "id": "221116" } - }, + }, { - "type": "image", - "location": null, + "type": "image", + "tags": [], + "location": null, "comments": { - "count": 6, + "count": 6, "data": [ { - "created_time": "1289314408", - "text": "ガラスフィルター", + "created_time": "1289314408", + "text": "ガラスフィルター", "from": { - "username": "mitsu93", - "first_name": "k", - "last_name": "mitsu", - "type": "user", + "username": "mitsu93", + "first_name": "k", + "last_name": "mitsu", + "type": "user", "id": "190905" - }, + }, "id": "2589455" - }, + }, { - "created_time": "1289315101", - "text": "glass filter :)", + "created_time": "1289315101", + "text": "glass filter :)", "from": { - "username": "mitsu93", - "first_name": "k", - "last_name": "mitsu", - "type": "user", + "username": "mitsu93", + "first_name": "k", + "last_name": "mitsu", + "type": "user", "id": "190905" - }, + }, "id": "2590951" - }, + }, { - "created_time": "1289315639", - "text": "リアルフィルター‼", + "created_time": "1289315639", + "text": "リアルフィルター‼", "from": { - "username": "lp1103", - "first_name": "L", - "last_name": "P", - "type": "user", + "username": "lp1103", + "first_name": "L", + "last_name": "P", + "type": "user", "id": "448405" - }, + }, "id": "2592060" - }, + }, { - "created_time": "1289315759", - "text": "Nice!", + "created_time": "1289315759", + "text": "Nice!", "from": { - "username": "xwing_red5", - "first_name": "xwing", - "last_name": "red5", - "type": "user", + "username": "xwing_red5", + "first_name": "xwing", + "last_name": "red5", + "type": "user", "id": "229322" - }, + }, "id": "2592326" - }, + }, { - "created_time": "1289316285", - "text": "(⺤‿⺤) ★♬★♬", + "created_time": "1289316285", + "text": "(⺤‿⺤) ★♬★♬", "from": { - "username": "001halno", - "first_name": "Halno", - "last_name": "Kujira", - "type": "user", + "username": "001halno", + "first_name": "Halno", + "last_name": "Kujira", + "type": "user", "id": "187383" - }, + }, "id": "2593472" - }, + }, { - "created_time": "1289318084", - "text": "www", + "created_time": "1289318084", + "text": "www", "from": { - "username": "currylove", - "first_name": "Curry", - "last_name": "Love", - "type": "user", + "username": "currylove", + "first_name": "Curry", + "last_name": "Love", + "type": "user", "id": "196692" - }, + }, "id": "2597020" } ] - }, + }, "caption": { - "created_time": "1289314408", - "text": "ガラスフィルター", + "created_time": "1289314408", + "text": "ガラスフィルター", "from": { - "username": "mitsu93", - "first_name": "k", - "last_name": "mitsu", - "type": "user", + "username": "mitsu93", + "first_name": "k", + "last_name": "mitsu", + "type": "user", "id": "190905" - }, + }, "id": "2589455" - }, - "link": "http://localhost:8000/p/M0nx/", + }, + "link": "http://localhost:8000/p/M0nx/", "likes": { "count": 31 - }, - "created_time": "1289314405", + }, + "created_time": "1289314405", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/b09ee3386e914a1cb1243e63a9a1fb3b_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/b09ee3386e914a1cb1243e63a9a1fb3b_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/b09ee3386e914a1cb1243e63a9a1fb3b_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/b09ee3386e914a1cb1243e63a9a1fb3b_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/b09ee3386e914a1cb1243e63a9a1fb3b_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/b09ee3386e914a1cb1243e63a9a1fb3b_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3361265", + }, + "user_has_liked": false, + "id": "3361265", "user": { - "username": "mitsu93", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_190905_75sq_1287807213.jpg", + "username": "mitsu93", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_190905_75sq_1287807213.jpg", "id": "190905" } - }, + }, { - "type": "image", - "location": null, + "type": "image", + "tags": [], + "location": null, "comments": { - "count": 4, + "count": 4, "data": [ { - "created_time": "1289323006", - "text": "Catcafe Calico \"Ruu\"", + "created_time": "1289323006", + "text": "Catcafe Calico \"Ruu\"", "from": { - "username": "wa_sabi", - "first_name": "wa_sabi", - "last_name": ".", - "type": "user", + "username": "wa_sabi", + "first_name": "wa_sabi", + "last_name": ".", + "type": "user", "id": "134240" - }, + }, "id": "2605651" - }, + }, { - "created_time": "1289323213", - "text": "Wow! Pretty!!", + "created_time": "1289323213", + "text": "Wow! Pretty!!", "from": { - "username": "nekosharon", - "first_name": "Sharon", - "last_name": "Toong", - "type": "user", + "username": "nekosharon", + "first_name": "Sharon", + "last_name": "Toong", + "type": "user", "id": "527296" - }, + }, "id": "2605956" - }, + }, { - "created_time": "1289323232", - "text": "Cute!", + "created_time": "1289323232", + "text": "Cute!", "from": { - "username": "nadenapa", - "first_name": "Nadenapa", - "last_name": "(^o^)", - "type": "user", + "username": "nadenapa", + "first_name": "Nadenapa", + "last_name": "(^o^)", + "type": "user", "id": "5147" - }, + }, "id": "2605982" - }, + }, { - "created_time": "1289323291", - "text": "I want to open my own catcafe!!", + "created_time": "1289323291", + "text": "I want to open my own catcafe!!", "from": { - "username": "lovejemma", - "first_name": "Jemma", - "last_name": "Allen", - "type": "user", + "username": "lovejemma", + "first_name": "Jemma", + "last_name": "Allen", + "type": "user", "id": "407520" - }, + }, "id": "2606069" } ] - }, + }, "caption": { - "created_time": "1289323006", - "text": "Catcafe Calico \"Ruu\"", + "created_time": "1289323006", + "text": "Catcafe Calico \"Ruu\"", "from": { - "username": "wa_sabi", - "first_name": "wa_sabi", - "last_name": ".", - "type": "user", + "username": "wa_sabi", + "first_name": "wa_sabi", + "last_name": ".", + "type": "user", "id": "134240" - }, + }, "id": "2605651" - }, - "link": "http://localhost:8000/p/M4cE/", + }, + "link": "http://localhost:8000/p/M4cE/", "likes": { "count": 15 - }, - "created_time": "1289322977", + }, + "created_time": "1289322977", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/d8c18aa6ee97411a8c36a5ffdcfd4c1e_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/d8c18aa6ee97411a8c36a5ffdcfd4c1e_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/d8c18aa6ee97411a8c36a5ffdcfd4c1e_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/d8c18aa6ee97411a8c36a5ffdcfd4c1e_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/d8c18aa6ee97411a8c36a5ffdcfd4c1e_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/d8c18aa6ee97411a8c36a5ffdcfd4c1e_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3376900", + }, + "user_has_liked": false, + "id": "3376900", "user": { - "username": "wa_sabi", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_134240_75sq_1288361859.jpg", + "username": "wa_sabi", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_134240_75sq_1288361859.jpg", "id": "134240" } - }, + }, { - "type": "image", - "location": null, + "type": "image", + "tags": [], + "location": null, "comments": { - "count": 10, + "count": 10, "data": [ { - "created_time": "1289038778", - "text": "お日様たっぷり浴びているね〜", + "created_time": "1289038778", + "text": "お日様たっぷり浴びているね〜", "from": { - "username": "munerin", - "first_name": "Mune", - "last_name": "Rin", - "type": "user", + "username": "munerin", + "first_name": "Mune", + "last_name": "Rin", + "type": "user", "id": "202364" - }, + }, "id": "2085010" - }, + }, { - "created_time": "1289040658", - "text": "お花みたいに綺麗!緑のお花。(*^^*)", + "created_time": "1289040658", + "text": "お花みたいに綺麗!緑のお花。(*^^*)", "from": { - "username": "kyococo", - "first_name": "Kyoco", - "last_name": "Yamayama", - "type": "user", + "username": "kyococo", + "first_name": "Kyoco", + "last_name": "Yamayama", + "type": "user", "id": "201350" - }, + }, "id": "2088497" - }, + }, { - "created_time": "1289311972", - "text": "フォローありがとうございます。美味しそう... (-_-;", + "created_time": "1289311972", + "text": "フォローありがとうございます。美味しそう... (-_-;", "from": { - "username": "hualna", - "first_name": "", - "last_name": "", - "type": "user", + "username": "hualna", + "first_name": "", + "last_name": "", + "type": "user", "id": "422142" - }, + }, "id": "2584276" - }, + }, { - "created_time": "1289312183", - "text": "ありがとう〜", + "created_time": "1289312183", + "text": "ありがとう〜", "from": { - "username": "shishimaruwo", - "first_name": "shishi", - "last_name": "maruwo", - "type": "user", + "username": "shishimaruwo", + "first_name": "shishi", + "last_name": "maruwo", + "type": "user", "id": "328227" - }, + }, "id": "2584714" - }, + }, { - "created_time": "1289312249", - "text": "Nice green!", + "created_time": "1289312249", + "text": "Nice green!", "from": { - "username": "sandakeyaki", - "first_name": "", - "last_name": "", - "type": "user", + "username": "sandakeyaki", + "first_name": "", + "last_name": "", + "type": "user", "id": "466962" - }, + }, "id": "2584854" - }, + }, { - "created_time": "1289312776", - "text": "キャベツ畑人形でもいそうな( ´ ▽ ` )ノ", + "created_time": "1289312776", + "text": "キャベツ畑人形でもいそうな( ´ ▽ ` )ノ", "from": { - "username": "ugajiny", - "first_name": "Uga", - "last_name": "Yoshi", - "type": "user", + "username": "ugajiny", + "first_name": "Uga", + "last_name": "Yoshi", + "type": "user", "id": "148077" - }, + }, "id": "2585966" - }, + }, { - "created_time": "1289315024", - "text": "フォローありがとうございます(^^)コレ素敵ですね。キレイな緑!", + "created_time": "1289315024", + "text": "フォローありがとうございます(^^)コレ素敵ですね。キレイな緑!", "from": { - "username": "rengring", - "first_name": "Konaka", - "last_name": "Toi", - "type": "user", + "username": "rengring", + "first_name": "Konaka", + "last_name": "Toi", + "type": "user", "id": "214712" - }, + }, "id": "2590780" - }, + }, { - "created_time": "1289317380", - "text": "心の綺麗な人には、見えます!キャベツの妖精(^ー^)ノちなみに私には見えません( ̄▽ ̄)", + "created_time": "1289317380", + "text": "心の綺麗な人には、見えます!キャベツの妖精(^ー^)ノちなみに私には見えません( ̄▽ ̄)", "from": { - "username": "shishimaruwo", - "first_name": "shishi", - "last_name": "maruwo", - "type": "user", + "username": "shishimaruwo", + "first_name": "shishi", + "last_name": "maruwo", + "type": "user", "id": "328227" - }, + }, "id": "2595597" - }, + }, { - "created_time": "1289322304", - "text": "私にもみえません…( ; ; )", + "created_time": "1289322304", + "text": "私にもみえません…( ; ; )", "from": { - "username": "snowstar", - "first_name": "Yuki", - "last_name": "Daruma", - "type": "user", + "username": "snowstar", + "first_name": "Yuki", + "last_name": "Daruma", + "type": "user", "id": "192441" - }, + }, "id": "2604576" - }, + }, { - "created_time": "1289323832", - "text": "Suddenly have a craving for salads", + "created_time": "1289323832", + "text": "Suddenly have a craving for salads", "from": { - "username": "mleemay", - "first_name": "Lee", - "last_name": "May", - "type": "user", + "username": "mleemay", + "first_name": "Lee", + "last_name": "May", + "type": "user", "id": "535093" - }, + }, "id": "2606852" } ] - }, - "caption": null, - "link": "http://localhost:8000/p/JsXP/", + }, + "caption": null, + "link": "http://localhost:8000/p/JsXP/", "likes": { "count": 65 - }, - "created_time": "1288909451", + }, + "created_time": "1288909451", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/04/ef9d2a1685734da7a0e5096cdb126796_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/04/ef9d2a1685734da7a0e5096cdb126796_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/04/ef9d2a1685734da7a0e5096cdb126796_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/04/ef9d2a1685734da7a0e5096cdb126796_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/04/ef9d2a1685734da7a0e5096cdb126796_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/04/ef9d2a1685734da7a0e5096cdb126796_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "2541007", + }, + "user_has_liked": false, + "id": "2541007", "user": { - "username": "shishimaruwo", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_328227_75sq_1289318615.jpg", + "username": "shishimaruwo", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_328227_75sq_1289318615.jpg", "id": "328227" } - }, + }, { - "type": "image", + "type": "image", + "tags": [], "location": { - "latitude": 35.669483999999997, - "id": "38157", - "longitude": 139.688635, + "latitude": 35.669483999999997, + "id": "38157", + "longitude": 139.688635, "name": "代々木八幡駅" - }, + }, "comments": { - "count": 1, + "count": 1, "data": [ { - "created_time": "1289316642", - "text": "Between concrete", + "created_time": "1289316642", + "text": "Between concrete", "from": { - "username": "shungo_a_lie", - "first_name": "Shungo", - "last_name": "Arai", - "type": "user", + "username": "shungo_a_lie", + "first_name": "Shungo", + "last_name": "Arai", + "type": "user", "id": "427321" - }, + }, "id": "2594214" } ] - }, + }, "caption": { - "created_time": "1289316642", - "text": "Between concrete", + "created_time": "1289316642", + "text": "Between concrete", "from": { - "username": "shungo_a_lie", - "first_name": "Shungo", - "last_name": "Arai", - "type": "user", + "username": "shungo_a_lie", + "first_name": "Shungo", + "last_name": "Arai", + "type": "user", "id": "427321" - }, + }, "id": "2594214" - }, - "link": "http://localhost:8000/p/M1qe/", + }, + "link": "http://localhost:8000/p/M1qe/", "likes": { "count": 10 - }, - "created_time": "1289316520", + }, + "created_time": "1289316520", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/32be663b17884aedaa53d634a6925060_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/32be663b17884aedaa53d634a6925060_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/32be663b17884aedaa53d634a6925060_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/32be663b17884aedaa53d634a6925060_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/32be663b17884aedaa53d634a6925060_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/32be663b17884aedaa53d634a6925060_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3365534", + }, + "user_has_liked": false, + "id": "3365534", "user": { - "username": "shungo_a_lie", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_427321_75sq_1288775392.jpg", + "username": "shungo_a_lie", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_427321_75sq_1288775392.jpg", "id": "427321" } - }, + }, { - "type": "image", - "location": null, + "type": "image", + "tags": [], + "location": null, "comments": { - "count": 10, + "count": 10, "data": [ { - "created_time": "1289259852", - "text": "M :-) Nice shot!!!", + "created_time": "1289259852", + "text": "M :-) Nice shot!!!", "from": { - "username": "akeligventje", - "first_name": "Peter", - "last_name": "Janssen", - "type": "user", + "username": "akeligventje", + "first_name": "Peter", + "last_name": "Janssen", + "type": "user", "id": "300035" - }, + }, "id": "2495378" - }, + }, { - "created_time": "1289260022", - "text": "Oh beautiful", + "created_time": "1289260022", + "text": "Oh beautiful", "from": { - "username": "quinzheecadle", - "first_name": "Quinzhee", - "last_name": "Candle", - "type": "user", + "username": "quinzheecadle", + "first_name": "Quinzhee", + "last_name": "Candle", + "type": "user", "id": "285405" - }, + }, "id": "2495691" - }, + }, { - "created_time": "1289262187", - "text": "Mac", + "created_time": "1289262187", + "text": "Mac", "from": { - "username": "onzou", - "first_name": "onzou", - "last_name": "♪", - "type": "user", + "username": "onzou", + "first_name": "onzou", + "last_name": "♪", + "type": "user", "id": "214126" - }, + }, "id": "2499726" - }, + }, { - "created_time": "1289262884", - "text": "Ba da bap Ba Ba I'm lovin it", + "created_time": "1289262884", + "text": "Ba da bap Ba Ba I'm lovin it", "from": { - "username": "justinprince", - "first_name": "Justin", - "last_name": "Prince", - "type": "user", + "username": "justinprince", + "first_name": "Justin", + "last_name": "Prince", + "type": "user", "id": "404977" - }, + }, "id": "2500931" - }, + }, { - "created_time": "1289262993", - "text": "Great pic.", + "created_time": "1289262993", + "text": "Great pic.", "from": { - "username": "carminalou", - "first_name": "Tim", - "last_name": "Williams", - "type": "user", + "username": "carminalou", + "first_name": "Tim", + "last_name": "Williams", + "type": "user", "id": "330803" - }, + }, "id": "2501097" - }, + }, { - "created_time": "1289293363", - "text": "Who ever thought maccas could be beautiful?", + "created_time": "1289293363", + "text": "Who ever thought maccas could be beautiful?", "from": { - "username": "avamehari", - "first_name": "", - "last_name": "", - "type": "user", + "username": "avamehari", + "first_name": "", + "last_name": "", + "type": "user", "id": "541984" - }, + }, "id": "2551825" - }, + }, { - "created_time": "1289294888", - "text": "Beautiful!!", + "created_time": "1289294888", + "text": "Beautiful!!", "from": { - "username": "gershwin", - "first_name": "Gersh", - "last_name": "Win", - "type": "user", + "username": "gershwin", + "first_name": "Gersh", + "last_name": "Win", + "type": "user", "id": "312872" - }, + }, "id": "2554001" - }, + }, { - "created_time": "1289295718", - "text": "Amazing", + "created_time": "1289295718", + "text": "Amazing", "from": { - "username": "shiori1031", - "first_name": "", - "last_name": "", - "type": "user", + "username": "shiori1031", + "first_name": "", + "last_name": "", + "type": "user", "id": "431140" - }, + }, "id": "2555117" - }, + }, { - "created_time": "1289302232", - "text": "Nice picture", + "created_time": "1289302232", + "text": "Nice picture", "from": { - "username": "ssarada313", - "first_name": "Taeho", - "last_name": "Jun", - "type": "user", + "username": "ssarada313", + "first_name": "Taeho", + "last_name": "Jun", + "type": "user", "id": "559426" - }, + }, "id": "2565207" - }, + }, { - "created_time": "1289325401", - "text": "thankyou!! :]", + "created_time": "1289325401", + "text": "thankyou!! :]", "from": { - "username": "hboo", - "first_name": "", - "last_name": "", - "type": "user", + "username": "hboo", + "first_name": "", + "last_name": "", + "type": "user", "id": "377036" - }, + }, "id": "2609166" } ] - }, - "caption": null, - "link": "http://localhost:8000/p/Mcvm/", + }, + "caption": null, + "link": "http://localhost:8000/p/Mcvm/", "likes": { "count": 144 - }, - "created_time": "1289259736", + }, + "created_time": "1289259736", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/03ed092aafeb46c9ac3b4f3e0c5ef411_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/03ed092aafeb46c9ac3b4f3e0c5ef411_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/03ed092aafeb46c9ac3b4f3e0c5ef411_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/03ed092aafeb46c9ac3b4f3e0c5ef411_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/03ed092aafeb46c9ac3b4f3e0c5ef411_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/03ed092aafeb46c9ac3b4f3e0c5ef411_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3263462", + }, + "user_has_liked": false, + "id": "3263462", "user": { - "username": "hboo", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_377036_75sq_1289090635.jpg", + "username": "hboo", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_377036_75sq_1289090635.jpg", "id": "377036" } - }, + }, { - "type": "image", - "location": null, + "type": "image", + "tags": [], + "location": null, "comments": { - "count": 17, + "count": 17, "data": [ { - "created_time": "1289322221", - "text": "とても素敵ですね☆", + "created_time": "1289322221", + "text": "とても素敵ですね☆", "from": { - "username": "reve", - "first_name": "lil", - "last_name": "", - "type": "user", + "username": "reve", + "first_name": "lil", + "last_name": "", + "type": "user", "id": "527650" - }, + }, "id": "2604451" - }, + }, { - "created_time": "1289322297", - "text": "♥ twilight ♥", + "created_time": "1289322297", + "text": "♥ twilight ♥", "from": { - "username": "megurogawa", - "first_name": "megurogawa", - "last_name": "☻*", - "type": "user", + "username": "megurogawa", + "first_name": "megurogawa", + "last_name": "☻*", + "type": "user", "id": "122497" - }, + }, "id": "2604566" - }, + }, { - "created_time": "1289322400", - "text": "ありがとうございます☺", + "created_time": "1289322400", + "text": "ありがとうございます☺", "from": { - "username": "megurogawa", - "first_name": "megurogawa", - "last_name": "☻*", - "type": "user", + "username": "megurogawa", + "first_name": "megurogawa", + "last_name": "☻*", + "type": "user", "id": "122497" - }, + }, "id": "2604729" - }, + }, { - "created_time": "1289322402", - "text": "Omg is that real", + "created_time": "1289322402", + "text": "Omg is that real", "from": { - "username": "irischerry", - "first_name": "Iris", - "last_name": "Zhao", - "type": "user", + "username": "irischerry", + "first_name": "Iris", + "last_name": "Zhao", + "type": "user", "id": "314048" - }, + }, "id": "2604736" - }, + }, { - "created_time": "1289322444", - "text": "Pretty colours!", + "created_time": "1289322444", + "text": "Pretty colours!", "from": { - "username": "yoep91", - "first_name": "Yolanda", - "last_name": "Schoemaker", - "type": "user", + "username": "yoep91", + "first_name": "Yolanda", + "last_name": "Schoemaker", + "type": "user", "id": "248631" - }, + }, "id": "2604813" - }, + }, { - "created_time": "1289322982", - "text": "う~いつも素敵な色♡好きです☆", + "created_time": "1289322982", + "text": "う~いつも素敵な色♡好きです☆", "from": { - "username": "caramelcocoa", - "first_name": "Caramel", - "last_name": "Cocoa☆", - "type": "user", + "username": "caramelcocoa", + "first_name": "Caramel", + "last_name": "Cocoa☆", + "type": "user", "id": "33265" - }, + }, "id": "2605615" - }, + }, { - "created_time": "1289323702", - "text": "This is really pretty! :)", + "created_time": "1289323702", + "text": "This is really pretty! :)", "from": { - "username": "poptartdoll", - "first_name": "Kisty", - "last_name": "Skywalker", - "type": "user", + "username": "poptartdoll", + "first_name": "Kisty", + "last_name": "Skywalker", + "type": "user", "id": "562132" - }, + }, "id": "2606644" - }, + }, { - "created_time": "1289323779", - "text": "Cute pic", + "created_time": "1289323779", + "text": "Cute pic", "from": { - "username": "ninenine", - "first_name": "Ninenine", - "last_name": "Nakasurakun", - "type": "user", + "username": "ninenine", + "first_name": "Ninenine", + "last_name": "Nakasurakun", + "type": "user", "id": "305930" - }, + }, "id": "2606758" - }, + }, { - "created_time": "1289324169", - "text": "wow~pretty colors!!!love it!", + "created_time": "1289324169", + "text": "wow~pretty colors!!!love it!", "from": { - "username": "vivi113", - "first_name": "Wei", - "last_name": "Wei", - "type": "user", + "username": "vivi113", + "first_name": "Wei", + "last_name": "Wei", + "type": "user", "id": "297863" - }, + }, "id": "2607337" - }, + }, { - "created_time": "1289324329", - "text": "ファンタジーみたいですね。", + "created_time": "1289324329", + "text": "ファンタジーみたいですね。", "from": { - "username": "liltasm", - "first_name": "Asami", - "last_name": "Ike", - "type": "user", + "username": "liltasm", + "first_name": "Asami", + "last_name": "Ike", + "type": "user", "id": "499161" - }, + }, "id": "2607583" - }, + }, { - "created_time": "1289324862", - "text": "So pretty. Juz like blend of watercolor", + "created_time": "1289324862", + "text": "So pretty. Juz like blend of watercolor", "from": { - "username": "cestlachoco", - "first_name": "Chrisna", - "last_name": "Chung", - "type": "user", + "username": "cestlachoco", + "first_name": "Chrisna", + "last_name": "Chung", + "type": "user", "id": "572653" - }, + }, "id": "2608333" - }, + }, { - "created_time": "1289325067", - "text": "すごくいいです!", + "created_time": "1289325067", + "text": "すごくいいです!", "from": { - "username": "hysaiuon", - "first_name": "hidetaka", - "last_name": "TASAKI", - "type": "user", + "username": "hysaiuon", + "first_name": "hidetaka", + "last_name": "TASAKI", + "type": "user", "id": "47367" - }, + }, "id": "2608682" - }, + }, { - "created_time": "1289325350", - "text": "色使いがキレイ ヾ(*´∀`*)ノ゙", + "created_time": "1289325350", + "text": "色使いがキレイ ヾ(*´∀`*)ノ゙", "from": { - "username": "kyooon", - "first_name": "Kyon", - "last_name": "M", - "type": "user", + "username": "kyooon", + "first_name": "Kyon", + "last_name": "M", + "type": "user", "id": "274447" - }, + }, "id": "2609087" - }, + }, { - "created_time": "1289325473", - "text": "Amazing!!!!", + "created_time": "1289325473", + "text": "Amazing!!!!", "from": { - "username": "hich", - "first_name": "", - "last_name": "", - "type": "user", + "username": "hich", + "first_name": "", + "last_name": "", + "type": "user", "id": "465037" - }, + }, "id": "2609282" - }, + }, { - "created_time": "1289325499", - "text": "Cute colouring :)", + "created_time": "1289325499", + "text": "Cute colouring :)", "from": { - "username": "hannahanna", - "first_name": "Hanna", - "last_name": "", - "type": "user", + "username": "hannahanna", + "first_name": "Hanna", + "last_name": "", + "type": "user", "id": "271373" - }, + }, "id": "2609319" - }, + }, { - "created_time": "1289325529", - "text": "Pastel colors!! Just so wonderful!", + "created_time": "1289325529", + "text": "Pastel colors!! Just so wonderful!", "from": { - "username": "megaera", - "first_name": "Teresa", - "last_name": "C", - "type": "user", + "username": "megaera", + "first_name": "Teresa", + "last_name": "C", + "type": "user", "id": "207272" - }, + }, "id": "2609369" - }, + }, { - "created_time": "1289325898", - "text": "Nice", + "created_time": "1289325898", + "text": "Nice", "from": { - "username": "jovimonkey", - "first_name": "Jovi", - "last_name": "", - "type": "user", + "username": "jovimonkey", + "first_name": "Jovi", + "last_name": "", + "type": "user", "id": "176982" - }, + }, "id": "2609842" } ] - }, - "caption": null, - "link": "http://localhost:8000/p/M4Hk/", + }, + "caption": null, + "link": "http://localhost:8000/p/M4Hk/", "likes": { "count": 121 - }, - "created_time": "1289322144", + }, + "created_time": "1289322144", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/530534a1e7dd473e98a622f5f0aea737_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/530534a1e7dd473e98a622f5f0aea737_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/530534a1e7dd473e98a622f5f0aea737_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/530534a1e7dd473e98a622f5f0aea737_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/530534a1e7dd473e98a622f5f0aea737_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/530534a1e7dd473e98a622f5f0aea737_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3375588", + }, + "user_has_liked": false, + "id": "3375588", "user": { - "username": "megurogawa", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_122497_75sq_1288341770.jpg", + "username": "megurogawa", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_122497_75sq_1288341770.jpg", "id": "122497" } - }, + }, { - "type": "image", - "location": null, + "type": "image", + "tags": [], + "location": null, "comments": { - "count": 4, + "count": 4, "data": [ { - "created_time": "1289316624", - "text": "Deep colors", + "created_time": "1289316624", + "text": "Deep colors", "from": { - "username": "guiltybystander", - "first_name": "Tony", - "last_name": "Pennington", - "type": "user", + "username": "guiltybystander", + "first_name": "Tony", + "last_name": "Pennington", + "type": "user", "id": "515161" - }, + }, "id": "2594168" - }, + }, { - "created_time": "1289316957", - "text": "Looks like the first day of a road trip...when you get up bright and early to begin the adventure.", + "created_time": "1289316957", + "text": "Looks like the first day of a road trip...when you get up bright and early to begin the adventure.", "from": { - "username": "petals17x2", - "first_name": "", - "last_name": "", - "type": "user", + "username": "petals17x2", + "first_name": "", + "last_name": "", + "type": "user", "id": "548126" - }, + }, "id": "2594797" - }, + }, { - "created_time": "1289317085", - "text": "@petals17x2, so true. Very crisp air!", + "created_time": "1289317085", + "text": "@petals17x2, so true. Very crisp air!", "from": { - "username": "guiltybystander", - "first_name": "Tony", - "last_name": "Pennington", - "type": "user", + "username": "guiltybystander", + "first_name": "Tony", + "last_name": "Pennington", + "type": "user", "id": "515161" - }, + }, "id": "2595037" - }, + }, { - "created_time": "1289318959", - "text": "That is almost too much! Such rich blues and stricking clouds. Love it!", + "created_time": "1289318959", + "text": "That is almost too much! Such rich blues and stricking clouds. Love it!", "from": { - "username": "fairiegirl", - "first_name": "", - "last_name": "", - "type": "user", + "username": "fairiegirl", + "first_name": "", + "last_name": "", + "type": "user", "id": "557169" - }, + }, "id": "2598641" } ] - }, + }, "caption": { - "created_time": "1289316624", - "text": "Deep colors", + "created_time": "1289316624", + "text": "Deep colors", "from": { - "username": "guiltybystander", - "first_name": "Tony", - "last_name": "Pennington", - "type": "user", + "username": "guiltybystander", + "first_name": "Tony", + "last_name": "Pennington", + "type": "user", "id": "515161" - }, + }, "id": "2594168" - }, - "link": "http://localhost:8000/p/M1tP/", + }, + "link": "http://localhost:8000/p/M1tP/", "likes": { "count": 10 - }, - "created_time": "1289316608", + }, + "created_time": "1289316608", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/21a9552630e649cd982f3b297c65784e_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/21a9552630e649cd982f3b297c65784e_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/21a9552630e649cd982f3b297c65784e_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/21a9552630e649cd982f3b297c65784e_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/21a9552630e649cd982f3b297c65784e_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/21a9552630e649cd982f3b297c65784e_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3365711", + }, + "user_has_liked": false, + "id": "3365711", "user": { - "username": "guiltybystander", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_515161_75sq_1289319751.jpg", + "username": "guiltybystander", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_515161_75sq_1289319751.jpg", "id": "515161" } - }, + }, { - "type": "image", - "location": null, + "type": "image", + "tags": [], + "location": null, "comments": { - "count": 12, + "count": 12, "data": [ { - "created_time": "1289317793", - "text": "Waiting for the film to begin...", + "created_time": "1289317793", + "text": "Waiting for the film to begin...", "from": { - "username": "mayli", - "first_name": "May-Li", - "last_name": "Khoe", - "type": "user", + "username": "mayli", + "first_name": "May-Li", + "last_name": "Khoe", + "type": "user", "id": "104804" - }, + }, "id": "2596439" - }, + }, { - "created_time": "1289317940", - "text": "I'm going to look for pop corn ... Don't love!", + "created_time": "1289317940", + "text": "I'm going to look for pop corn ... Don't love!", "from": { - "username": "zimzam", - "first_name": "", - "last_name": "", - "type": "user", + "username": "zimzam", + "first_name": "", + "last_name": "", + "type": "user", "id": "393250" - }, + }, "id": "2596741" - }, + }, { - "created_time": "1289317963", - "text": "Very nice! Like this one alot!", + "created_time": "1289317963", + "text": "Very nice! Like this one alot!", "from": { - "username": "ballchain", - "first_name": "Harald", - "last_name": "Nilsson", - "type": "user", + "username": "ballchain", + "first_name": "Harald", + "last_name": "Nilsson", + "type": "user", "id": "53914" - }, + }, "id": "2596786" - }, + }, { - "created_time": "1289318044", - "text": "Don't love! = don't move.. Sorry correction by iPhone correction... :)", + "created_time": "1289318044", + "text": "Don't love! = don't move.. Sorry correction by iPhone correction... :)", "from": { - "username": "zimzam", - "first_name": "", - "last_name": "", - "type": "user", + "username": "zimzam", + "first_name": "", + "last_name": "", + "type": "user", "id": "393250" - }, + }, "id": "2596935" - }, + }, { - "created_time": "1289318580", - "text": "Magnificent image. Love it.", + "created_time": "1289318580", + "text": "Magnificent image. Love it.", "from": { - "username": "fashion", - "first_name": "Mal", - "last_name": "Sherlock", - "type": "user", + "username": "fashion", + "first_name": "Mal", + "last_name": "Sherlock", + "type": "user", "id": "123395" - }, + }, "id": "2597929" - }, + }, { - "created_time": "1289318635", - "text": "I really like scene and feel. nice capture!", + "created_time": "1289318635", + "text": "I really like scene and feel. nice capture!", "from": { - "username": "transitionpete", - "first_name": "pete", - "last_name": "kim", - "type": "user", + "username": "transitionpete", + "first_name": "pete", + "last_name": "kim", + "type": "user", "id": "15023" - }, + }, "id": "2598021" - }, + }, { - "created_time": "1289318712", - "text": "Cool", + "created_time": "1289318712", + "text": "Cool", "from": { - "username": "julian90405", - "first_name": "Julian", - "last_name": "G", - "type": "user", + "username": "julian90405", + "first_name": "Julian", + "last_name": "G", + "type": "user", "id": "119880" - }, + }, "id": "2598188" - }, + }, { - "created_time": "1289319036", - "text": "Wow! Like it X 1,000,000!", + "created_time": "1289319036", + "text": "Wow! Like it X 1,000,000!", "from": { - "username": "albertlaw", - "first_name": "LAW", - "last_name": "Albert", - "type": "user", + "username": "albertlaw", + "first_name": "LAW", + "last_name": "Albert", + "type": "user", "id": "390956" - }, + }, "id": "2598774" - }, + }, { - "created_time": "1289319216", - "text": "Aw, thank you so much!", + "created_time": "1289319216", + "text": "Aw, thank you so much!", "from": { - "username": "mayli", - "first_name": "May-Li", - "last_name": "Khoe", - "type": "user", + "username": "mayli", + "first_name": "May-Li", + "last_name": "Khoe", + "type": "user", "id": "104804" - }, + }, "id": "2599096" - }, + }, { - "created_time": "1289319456", - "text": "Love the mood here", + "created_time": "1289319456", + "text": "Love the mood here", "from": { - "username": "nea", - "first_name": "Linnéa", - "last_name": "Löfdahl", - "type": "user", + "username": "nea", + "first_name": "Linnéa", + "last_name": "Löfdahl", + "type": "user", "id": "2507" - }, + }, "id": "2599559" - }, + }, { - "created_time": "1289320739", - "text": "Shot this before but it didn't work. Like yours much better. :)", + "created_time": "1289320739", + "text": "Shot this before but it didn't work. Like yours much better. :)", "from": { - "username": "joshjohnson", - "first_name": "", - "last_name": "INSTATIPS", - "type": "user", + "username": "joshjohnson", + "first_name": "", + "last_name": "INSTATIPS", + "type": "user", "id": "98476" - }, + }, "id": "2601996" - }, + }, { - "created_time": "1289323156", - "text": "Cool", + "created_time": "1289323156", + "text": "Cool", "from": { - "username": "shin_ht41", - "first_name": "shin", - "last_name": "", - "type": "user", + "username": "shin_ht41", + "first_name": "shin", + "last_name": "", + "type": "user", "id": "235289" - }, + }, "id": "2605869" } ] - }, + }, "caption": { - "created_time": "1289317793", - "text": "Waiting for the film to begin...", + "created_time": "1289317793", + "text": "Waiting for the film to begin...", "from": { - "username": "mayli", - "first_name": "May-Li", - "last_name": "Khoe", - "type": "user", + "username": "mayli", + "first_name": "May-Li", + "last_name": "Khoe", + "type": "user", "id": "104804" - }, + }, "id": "2596439" - }, - "link": "http://localhost:8000/p/M2Pz/", + }, + "link": "http://localhost:8000/p/M2Pz/", "likes": { "count": 33 - }, - "created_time": "1289317782", + }, + "created_time": "1289317782", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/ce8c73e1fdb1409488153ec7019232d6_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/ce8c73e1fdb1409488153ec7019232d6_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/ce8c73e1fdb1409488153ec7019232d6_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/ce8c73e1fdb1409488153ec7019232d6_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/ce8c73e1fdb1409488153ec7019232d6_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/ce8c73e1fdb1409488153ec7019232d6_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3367923", + }, + "user_has_liked": false, + "id": "3367923", "user": { - "username": "mayli", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_104804_75sq_1288548631.jpg", + "username": "mayli", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_104804_75sq_1288548631.jpg", "id": "104804" } - }, + }, { - "type": "image", - "location": null, + "type": "image", + "tags": [], + "location": null, "comments": { - "count": 8, + "count": 8, "data": [ { - "created_time": "1289265094", - "text": "124", + "created_time": "1289265094", + "text": "124", "from": { - "username": "francesccoves", - "first_name": "Francesc", - "last_name": "Coves", - "type": "user", + "username": "francesccoves", + "first_name": "Francesc", + "last_name": "Coves", + "type": "user", "id": "32887" - }, + }, "id": "2504886" - }, + }, { - "created_time": "1289267339", - "text": "Double exposure? If not my brain hurts...", + "created_time": "1289267339", + "text": "Double exposure? If not my brain hurts...", "from": { - "username": "missmagee", - "first_name": "", - "last_name": "", - "type": "user", + "username": "missmagee", + "first_name": "", + "last_name": "", + "type": "user", "id": "402953" - }, + }, "id": "2508826" - }, + }, { - "created_time": "1289268504", - "text": "I love it!!", + "created_time": "1289268504", + "text": "I love it!!", "from": { - "username": "gershwin", - "first_name": "Gersh", - "last_name": "Win", - "type": "user", + "username": "gershwin", + "first_name": "Gersh", + "last_name": "Win", + "type": "user", "id": "312872" - }, + }, "id": "2510895" - }, + }, { - "created_time": "1289269190", - "text": "I love this :)", + "created_time": "1289269190", + "text": "I love this :)", "from": { - "username": "kay101", - "first_name": "", - "last_name": "", - "type": "user", + "username": "kay101", + "first_name": "", + "last_name": "", + "type": "user", "id": "544295" - }, + }, "id": "2512097" - }, + }, { - "created_time": "1289270738", - "text": "These are some truely imaginitive shots! I dig it", + "created_time": "1289270738", + "text": "These are some truely imaginitive shots! I dig it", "from": { - "username": "typql", - "first_name": "", - "last_name": "", - "type": "user", + "username": "typql", + "first_name": "", + "last_name": "", + "type": "user", "id": "321299" - }, + }, "id": "2514785" - }, + }, { - "created_time": "1289277297", - "text": "That's so cool!!!!", + "created_time": "1289277297", + "text": "That's so cool!!!!", "from": { - "username": "analia", - "first_name": "Analía", - "last_name": "Vulich", - "type": "user", + "username": "analia", + "first_name": "Analía", + "last_name": "Vulich", + "type": "user", "id": "404088" - }, + }, "id": "2527158" - }, + }, { - "created_time": "1289287357", - "text": "Dig your double exposures, man.", + "created_time": "1289287357", + "text": "Dig your double exposures, man.", "from": { - "username": "dayzdandconfuzd", - "first_name": "David ", - "last_name": "Ingraham", - "type": "user", + "username": "dayzdandconfuzd", + "first_name": "David ", + "last_name": "Ingraham", + "type": "user", "id": "334112" - }, + }, "id": "2543119" - }, + }, { - "created_time": "1289317732", - "text": "Magical", + "created_time": "1289317732", + "text": "Magical", "from": { - "username": "torres", - "first_name": "Manuel", - "last_name": "Francisco", - "type": "user", + "username": "torres", + "first_name": "Manuel", + "last_name": "Francisco", + "type": "user", "id": "57111" - }, + }, "id": "2596304" } ] - }, + }, "caption": { - "created_time": "1289265094", - "text": "124", + "created_time": "1289265094", + "text": "124", "from": { - "username": "francesccoves", - "first_name": "Francesc", - "last_name": "Coves", - "type": "user", + "username": "francesccoves", + "first_name": "Francesc", + "last_name": "Coves", + "type": "user", "id": "32887" - }, + }, "id": "2504886" - }, - "link": "http://localhost:8000/p/MfME/", + }, + "link": "http://localhost:8000/p/MfME/", "likes": { "count": 94 - }, - "created_time": "1289265088", + }, + "created_time": "1289265088", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/54ffa628320e4fc3952bfcb7334ec1ae_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/54ffa628320e4fc3952bfcb7334ec1ae_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/54ffa628320e4fc3952bfcb7334ec1ae_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/54ffa628320e4fc3952bfcb7334ec1ae_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/54ffa628320e4fc3952bfcb7334ec1ae_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/54ffa628320e4fc3952bfcb7334ec1ae_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3273476", + }, + "user_has_liked": false, + "id": "3273476", "user": { - "username": "francesccoves", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_32887_75sq_1286553833.jpg", + "username": "francesccoves", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_32887_75sq_1286553833.jpg", "id": "32887" } - }, + }, { - "type": "image", - "location": null, + "type": "image", + "tags": [], + "location": null, "comments": { - "count": 12, + "count": 12, "data": [ { - "created_time": "1289317836", - "text": "女子カメラNIGHT〜girls camera night〜", + "created_time": "1289317836", + "text": "女子カメラNIGHT〜girls camera night〜", "from": { - "username": "blue_b", - "first_name": "masami", - "last_name": "yamamoto", - "type": "user", + "username": "blue_b", + "first_name": "masami", + "last_name": "yamamoto", + "type": "user", "id": "87011" - }, + }, "id": "2596521" - }, + }, { - "created_time": "1289317883", - "text": "Great shot!!", + "created_time": "1289317883", + "text": "Great shot!!", "from": { - "username": "gershwin", - "first_name": "Gersh", - "last_name": "Win", - "type": "user", + "username": "gershwin", + "first_name": "Gersh", + "last_name": "Win", + "type": "user", "id": "312872" - }, + }, "id": "2596634" - }, + }, { - "created_time": "1289317953", - "text": "I absolutely love this image", + "created_time": "1289317953", + "text": "I absolutely love this image", "from": { - "username": "hollandphotostudio", - "first_name": "Phil", - "last_name": "Holland", - "type": "user", + "username": "hollandphotostudio", + "first_name": "Phil", + "last_name": "Holland", + "type": "user", "id": "339511" - }, + }, "id": "2596763" - }, + }, { - "created_time": "1289318151", - "text": "Awesome", + "created_time": "1289318151", + "text": "Awesome", "from": { - "username": "andyl", - "first_name": "Andy", - "last_name": "Lovegrove", - "type": "user", + "username": "andyl", + "first_name": "Andy", + "last_name": "Lovegrove", + "type": "user", "id": "274313" - }, + }, "id": "2597155" - }, + }, { - "created_time": "1289318350", - "text": "ガールズカメラ会なんてうらやましい!!", + "created_time": "1289318350", + "text": "ガールズカメラ会なんてうらやましい!!", "from": { - "username": "uixm", - "first_name": "Yui", - "last_name": "MORI", - "type": "user", + "username": "uixm", + "first_name": "Yui", + "last_name": "MORI", + "type": "user", "id": "214197" - }, + }, "id": "2597517" - }, + }, { - "created_time": "1289318514", - "text": "すごい好きな感じ。", + "created_time": "1289318514", + "text": "すごい好きな感じ。", "from": { - "username": "yagimilk", - "first_name": "Milk", - "last_name": "Yagi", - "type": "user", + "username": "yagimilk", + "first_name": "Milk", + "last_name": "Yagi", + "type": "user", "id": "231493" - }, + }, "id": "2597817" - }, + }, { - "created_time": "1289320703", - "text": "ベリーナイス(▰˘◡˘▰)★♬", + "created_time": "1289320703", + "text": "ベリーナイス(▰˘◡˘▰)★♬", "from": { - "username": "001halno", - "first_name": "Halno", - "last_name": "Kujira", - "type": "user", + "username": "001halno", + "first_name": "Halno", + "last_name": "Kujira", + "type": "user", "id": "187383" - }, + }, "id": "2601933" - }, + }, { - "created_time": "1289320714", - "text": "!(◎_◎;)", + "created_time": "1289320714", + "text": "!(◎_◎;)", "from": { - "username": "officesagawa", - "first_name": "Hiroki", - "last_name": "Sagawa", - "type": "user", + "username": "officesagawa", + "first_name": "Hiroki", + "last_name": "Sagawa", + "type": "user", "id": "378086" - }, + }, "id": "2601954" - }, + }, { - "created_time": "1289321001", - "text": "おー。女子カメラ会すてき!やってみたいです:)", + "created_time": "1289321001", + "text": "おー。女子カメラ会すてき!やってみたいです:)", "from": { - "username": "maako", - "first_name": "maako", - "last_name": "tazawa", - "type": "user", + "username": "maako", + "first_name": "maako", + "last_name": "tazawa", + "type": "user", "id": "63567" - }, + }, "id": "2602502" - }, + }, { - "created_time": "1289322235", - "text": "かっこいい!", + "created_time": "1289322235", + "text": "かっこいい!", "from": { - "username": "caramelcocoa", - "first_name": "Caramel", - "last_name": "Cocoa☆", - "type": "user", + "username": "caramelcocoa", + "first_name": "Caramel", + "last_name": "Cocoa☆", + "type": "user", "id": "33265" - }, + }, "id": "2604471" - }, + }, { - "created_time": "1289323665", - "text": "スゴイかっこいい!", + "created_time": "1289323665", + "text": "スゴイかっこいい!", "from": { - "username": "main", - "first_name": "mai", - "last_name": "n", - "type": "user", + "username": "main", + "first_name": "mai", + "last_name": "n", + "type": "user", "id": "219734" - }, + }, "id": "2606600" - }, + }, { - "created_time": "1289324518", - "text": "女子と聞くと過剰に野郎は反応するよな~!あ!僕もその1人だ!ϵ( 'Θ' )϶", + "created_time": "1289324518", + "text": "女子と聞くと過剰に野郎は反応するよな~!あ!僕もその1人だ!ϵ( 'Θ' )϶", "from": { - "username": "plusbiz", - "first_name": "plus", - "last_name": "biz", - "type": "user", + "username": "plusbiz", + "first_name": "plus", + "last_name": "biz", + "type": "user", "id": "193950" - }, + }, "id": "2607832" } ] - }, + }, "caption": { - "created_time": "1289317836", - "text": "女子カメラNIGHT〜girls camera night〜", + "created_time": "1289317836", + "text": "女子カメラNIGHT〜girls camera night〜", "from": { - "username": "blue_b", - "first_name": "masami", - "last_name": "yamamoto", - "type": "user", + "username": "blue_b", + "first_name": "masami", + "last_name": "yamamoto", + "type": "user", "id": "87011" - }, + }, "id": "2596521" - }, - "link": "http://localhost:8000/p/M2Pf/", + }, + "link": "http://localhost:8000/p/M2Pf/", "likes": { "count": 52 - }, - "created_time": "1289317772", + }, + "created_time": "1289317772", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/5925e0e1c2a14fdba87013ca75887856_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/5925e0e1c2a14fdba87013ca75887856_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/5925e0e1c2a14fdba87013ca75887856_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/5925e0e1c2a14fdba87013ca75887856_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/5925e0e1c2a14fdba87013ca75887856_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/5925e0e1c2a14fdba87013ca75887856_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3367903", + }, + "user_has_liked": false, + "id": "3367903", "user": { - "username": "blue_b", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_87011_75sq_1288880751.jpg", + "username": "blue_b", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_87011_75sq_1288880751.jpg", "id": "87011" } - }, + }, { - "type": "image", - "location": null, + "type": "image", + "tags": [], + "location": null, "comments": { - "count": 14, + "count": 14, "data": [ { - "created_time": "1289321094", - "text": "Is it a bird, is it a plane?...", + "created_time": "1289321094", + "text": "Is it a bird, is it a plane?...", "from": { - "username": "antarctica", - "first_name": "", - "last_name": "", - "type": "user", + "username": "antarctica", + "first_name": "", + "last_name": "", + "type": "user", "id": "91589" - }, + }, "id": "2602659" - }, + }, { - "created_time": "1289321235", - "text": "Great shot!", + "created_time": "1289321235", + "text": "Great shot!", "from": { - "username": "wa_sabi", - "first_name": "wa_sabi", - "last_name": ".", - "type": "user", + "username": "wa_sabi", + "first_name": "wa_sabi", + "last_name": ".", + "type": "user", "id": "134240" - }, + }, "id": "2602876" - }, + }, { - "created_time": "1289321310", - "text": "Love this!", + "created_time": "1289321310", + "text": "Love this!", "from": { - "username": "luvzgreennblue", - "first_name": "Samantha", - "last_name": "", - "type": "user", + "username": "luvzgreennblue", + "first_name": "Samantha", + "last_name": "", + "type": "user", "id": "40768" - }, + }, "id": "2603009" - }, + }, { - "created_time": "1289321599", - "text": "Love this.", + "created_time": "1289321599", + "text": "Love this.", "from": { - "username": "mikeylynn", - "first_name": "Michallynn", - "last_name": "V", - "type": "user", + "username": "mikeylynn", + "first_name": "Michallynn", + "last_name": "V", + "type": "user", "id": "205889" - }, + }, "id": "2603448" - }, + }, { - "created_time": "1289321996", - "text": "FaB", + "created_time": "1289321996", + "text": "FaB", "from": { - "username": "ostelios", - "first_name": "O", - "last_name": "Stelios", - "type": "user", + "username": "ostelios", + "first_name": "O", + "last_name": "Stelios", + "type": "user", "id": "12208" - }, + }, "id": "2604097" - }, + }, { - "created_time": "1289322075", - "text": "It's both!!! Now all we have to do is wait for superman!!!:)", + "created_time": "1289322075", + "text": "It's both!!! Now all we have to do is wait for superman!!!:)", "from": { - "username": "e_marie", - "first_name": "Erin", - "last_name": "Marie", - "type": "user", + "username": "e_marie", + "first_name": "Erin", + "last_name": "Marie", + "type": "user", "id": "216711" - }, + }, "id": "2604216" - }, + }, { - "created_time": "1289322119", - "text": "I like it", + "created_time": "1289322119", + "text": "I like it", "from": { - "username": "reve", - "first_name": "lil", - "last_name": "", - "type": "user", + "username": "reve", + "first_name": "lil", + "last_name": "", + "type": "user", "id": "527650" - }, + }, "id": "2604292" - }, + }, { - "created_time": "1289322476", - "text": "like it!", + "created_time": "1289322476", + "text": "like it!", "from": { - "username": "nog", - "first_name": "M", - "last_name": "nog", - "type": "user", + "username": "nog", + "first_name": "M", + "last_name": "nog", + "type": "user", "id": "387597" - }, + }, "id": "2604867" - }, + }, { - "created_time": "1289322628", - "text": "perfect!", + "created_time": "1289322628", + "text": "perfect!", "from": { - "username": "transitionpete", - "first_name": "pete", - "last_name": "kim", - "type": "user", + "username": "transitionpete", + "first_name": "pete", + "last_name": "kim", + "type": "user", "id": "15023" - }, + }, "id": "2605099" - }, + }, { - "created_time": "1289324041", - "text": "I love it ♡", + "created_time": "1289324041", + "text": "I love it ♡", "from": { - "username": "lechat8", - "first_name": "Shino", - "last_name": "", - "type": "user", + "username": "lechat8", + "first_name": "Shino", + "last_name": "", + "type": "user", "id": "288248" - }, + }, "id": "2607135" - }, + }, { - "created_time": "1289324114", - "text": "That's awesome", + "created_time": "1289324114", + "text": "That's awesome", "from": { - "username": "sd_36", - "first_name": "", - "last_name": "", - "type": "user", + "username": "sd_36", + "first_name": "", + "last_name": "", + "type": "user", "id": "456770" - }, + }, "id": "2607250" - }, + }, { - "created_time": "1289325220", - "text": "I like the sky with birds♥", + "created_time": "1289325220", + "text": "I like the sky with birds♥", "from": { - "username": "chicaak", - "first_name": "cc", - "last_name": "kk", - "type": "user", + "username": "chicaak", + "first_name": "cc", + "last_name": "kk", + "type": "user", "id": "546915" - }, + }, "id": "2608904" - }, + }, { - "created_time": "1289325351", - "text": "Fantastic shot", + "created_time": "1289325351", + "text": "Fantastic shot", "from": { - "username": "mamamudoko", - "first_name": "", - "last_name": "", - "type": "user", + "username": "mamamudoko", + "first_name": "", + "last_name": "", + "type": "user", "id": "321508" - }, + }, "id": "2609090" - }, + }, { - "created_time": "1289325544", - "text": "Wow. I love it", + "created_time": "1289325544", + "text": "Wow. I love it", "from": { - "username": "no_8", - "first_name": "", - "last_name": "", - "type": "user", + "username": "no_8", + "first_name": "", + "last_name": "", + "type": "user", "id": "371461" - }, + }, "id": "2609395" } ] - }, + }, "caption": { - "created_time": "1289321094", - "text": "Is it a bird, is it a plane?...", + "created_time": "1289321094", + "text": "Is it a bird, is it a plane?...", "from": { - "username": "antarctica", - "first_name": "", - "last_name": "", - "type": "user", + "username": "antarctica", + "first_name": "", + "last_name": "", + "type": "user", "id": "91589" - }, + }, "id": "2602659" - }, - "link": "http://localhost:8000/p/M3sd/", + }, + "link": "http://localhost:8000/p/M3sd/", "likes": { "count": 125 - }, - "created_time": "1289321088", + }, + "created_time": "1289321088", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/0883a368919b4ca7b7575f7e12ea7496_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/0883a368919b4ca7b7575f7e12ea7496_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/0883a368919b4ca7b7575f7e12ea7496_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/0883a368919b4ca7b7575f7e12ea7496_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/0883a368919b4ca7b7575f7e12ea7496_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/0883a368919b4ca7b7575f7e12ea7496_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3373853", + }, + "user_has_liked": false, + "id": "3373853", "user": { - "username": "antarctica", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_91589_75sq_1287409562.jpg", + "username": "antarctica", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_91589_75sq_1287409562.jpg", "id": "91589" } - }, + }, { - "type": "image", - "location": null, + "type": "image", + "tags": [], + "location": null, "comments": { - "count": 4, + "count": 4, "data": [ { - "created_time": "1289324835", - "text": "Washed out Skeleton Key. Developed in SwankoLab with Contra Force, Film border added in Infinicam", + "created_time": "1289324835", + "text": "Washed out Skeleton Key. Developed in SwankoLab with Contra Force, Film border added in Infinicam", "from": { - "username": "poeticaesthetic", - "first_name": "Kristen ", - "last_name": "", - "type": "user", + "username": "poeticaesthetic", + "first_name": "Kristen ", + "last_name": "", + "type": "user", "id": "4279" - }, + }, "id": "2608282" - }, + }, { - "created_time": "1289325086", - "text": "Wow beautiful", + "created_time": "1289325086", + "text": "Wow beautiful", "from": { - "username": "iphonequeen", - "first_name": "Jane", - "last_name": "Jones", - "type": "user", + "username": "iphonequeen", + "first_name": "Jane", + "last_name": "Jones", + "type": "user", "id": "6392" - }, + }, "id": "2608710" - }, + }, { - "created_time": "1289325284", - "text": "Love it! :)", + "created_time": "1289325284", + "text": "Love it! :)", "from": { - "username": "curiouseindv", - "first_name": "", - "last_name": "", - "type": "user", + "username": "curiouseindv", + "first_name": "", + "last_name": "", + "type": "user", "id": "468723" - }, + }, "id": "2608996" - }, + }, { - "created_time": "1289325799", - "text": "Wow it's sooo beautiful..", + "created_time": "1289325799", + "text": "Wow it's sooo beautiful..", "from": { - "username": "neo121", - "first_name": "Evy", - "last_name": "Olivia", - "type": "user", + "username": "neo121", + "first_name": "Evy", + "last_name": "Olivia", + "type": "user", "id": "4124" - }, + }, "id": "2609719" } ] - }, + }, "caption": { - "created_time": "1289324835", - "text": "Washed out Skeleton Key. Developed in SwankoLab with Contra Force, Film border added in Infinicam", + "created_time": "1289324835", + "text": "Washed out Skeleton Key. Developed in SwankoLab with Contra Force, Film border added in Infinicam", "from": { - "username": "poeticaesthetic", - "first_name": "Kristen ", - "last_name": "", - "type": "user", + "username": "poeticaesthetic", + "first_name": "Kristen ", + "last_name": "", + "type": "user", "id": "4279" - }, + }, "id": "2608282" - }, - "link": "http://localhost:8000/p/M5Gp/", + }, + "link": "http://localhost:8000/p/M5Gp/", "likes": { "count": 14 - }, - "created_time": "1289324737", + }, + "created_time": "1289324737", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/02cb1cde9a62422a8bfb0aee552f0b07_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/02cb1cde9a62422a8bfb0aee552f0b07_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/02cb1cde9a62422a8bfb0aee552f0b07_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/02cb1cde9a62422a8bfb0aee552f0b07_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/02cb1cde9a62422a8bfb0aee552f0b07_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/02cb1cde9a62422a8bfb0aee552f0b07_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3379625", + }, + "user_has_liked": false, + "id": "3379625", "user": { - "username": "poeticaesthetic", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_4279_75sq_1289261907.jpg", + "username": "poeticaesthetic", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_4279_75sq_1289261907.jpg", "id": "4279" } - }, + }, { - "type": "image", + "type": "image", + "tags": [], "location": { - "latitude": null, - "id": "242629", - "longitude": null, + "latitude": null, + "id": "242629", + "longitude": null, "name": "My aquarium" - }, + }, "comments": { - "count": 23, + "count": 23, "data": [ { - "created_time": "1289256909", - "text": "Clown fish", + "created_time": "1289256909", + "text": "Clown fish", "from": { - "username": "sirhc1", - "first_name": "", - "last_name": "", - "type": "user", + "username": "sirhc1", + "first_name": "", + "last_name": "", + "type": "user", "id": "423404" - }, + }, "id": "2489109" - }, + }, { - "created_time": "1289257109", - "text": "I love this shot! Great job!", + "created_time": "1289257109", + "text": "I love this shot! Great job!", "from": { - "username": "ryltar", - "first_name": "Lorenzo", - "last_name": "", - "type": "user", + "username": "ryltar", + "first_name": "Lorenzo", + "last_name": "", + "type": "user", "id": "421198" - }, + }, "id": "2489567" - }, + }, { - "created_time": "1289257166", - "text": "Wow cute", + "created_time": "1289257166", + "text": "Wow cute", "from": { - "username": "felicity88", - "first_name": "Kimberley Felicity", - "last_name": "", - "type": "user", + "username": "felicity88", + "first_name": "Kimberley Felicity", + "last_name": "", + "type": "user", "id": "520398" - }, + }, "id": "2489712" - }, + }, { - "created_time": "1289257220", - "text": "Wow!", + "created_time": "1289257220", + "text": "Wow!", "from": { - "username": "guiltybystander", - "first_name": "Tony", - "last_name": "Pennington", - "type": "user", + "username": "guiltybystander", + "first_name": "Tony", + "last_name": "Pennington", + "type": "user", "id": "515161" - }, + }, "id": "2489821" - }, + }, { - "created_time": "1289257299", - "text": "Finding Nemo!", + "created_time": "1289257299", + "text": "Finding Nemo!", "from": { - "username": "hirochika", - "first_name": "Hirochika", - "last_name": "Asahara", - "type": "user", + "username": "hirochika", + "first_name": "Hirochika", + "last_name": "Asahara", + "type": "user", "id": "183649" - }, + }, "id": "2489995" - }, + }, { - "created_time": "1289257567", - "text": "Great shot!!", + "created_time": "1289257567", + "text": "Great shot!!", "from": { - "username": "leprechaun1", - "first_name": "", - "last_name": "", - "type": "user", + "username": "leprechaun1", + "first_name": "", + "last_name": "", + "type": "user", "id": "373482" - }, + }, "id": "2490588" - }, + }, { - "created_time": "1289257927", - "text": "I found Nemo too!!! Cute", + "created_time": "1289257927", + "text": "I found Nemo too!!! Cute", "from": { - "username": "sachii", - "first_name": "", - "last_name": "", - "type": "user", + "username": "sachii", + "first_name": "", + "last_name": "", + "type": "user", "id": "512202" - }, + }, "id": "2491331" - }, + }, { - "created_time": "1289257964", - "text": "Nice one", + "created_time": "1289257964", + "text": "Nice one", "from": { - "username": "bigjeff", - "first_name": "Jeff", - "last_name": "Pearson", - "type": "user", + "username": "bigjeff", + "first_name": "Jeff", + "last_name": "Pearson", + "type": "user", "id": "525791" - }, + }, "id": "2491410" - }, + }, { - "created_time": "1289258528", - "text": "可愛いねー♪", + "created_time": "1289258528", + "text": "可愛いねー♪", "from": { - "username": "aoisora0722", - "first_name": "aoisora", - "last_name": "", - "type": "user", + "username": "aoisora0722", + "first_name": "aoisora", + "last_name": "", + "type": "user", "id": "183223" - }, + }, "id": "2492635" - }, + }, { - "created_time": "1289259933", - "text": "Cool", + "created_time": "1289259933", + "text": "Cool", "from": { - "username": "roguestatus", - "first_name": "Shane", - "last_name": "", - "type": "user", + "username": "roguestatus", + "first_name": "Shane", + "last_name": "", + "type": "user", "id": "427701" - }, + }, "id": "2495525" - }, + }, { - "created_time": "1289259946", - "text": "Vivid pic!", + "created_time": "1289259946", + "text": "Vivid pic!", "from": { - "username": "carminalou", - "first_name": "Tim", - "last_name": "Williams", - "type": "user", + "username": "carminalou", + "first_name": "Tim", + "last_name": "Williams", + "type": "user", "id": "330803" - }, + }, "id": "2495545" - }, + }, { - "created_time": "1289260530", - "text": "Thanks for following!!!", + "created_time": "1289260530", + "text": "Thanks for following!!!", "from": { - "username": "gokkey2000", - "first_name": "Masato", - "last_name": "in KAGAWA", - "type": "user", + "username": "gokkey2000", + "first_name": "Masato", + "last_name": "in KAGAWA", + "type": "user", "id": "365715" - }, + }, "id": "2496682" - }, + }, { - "created_time": "1289260639", - "text": "Really nice pics that you have :)", + "created_time": "1289260639", + "text": "Really nice pics that you have :)", "from": { - "username": "thomas_k", - "first_name": "Thomas", - "last_name": "Kakareko", - "type": "user", + "username": "thomas_k", + "first_name": "Thomas", + "last_name": "Kakareko", + "type": "user", "id": "165018" - }, + }, "id": "2496887" - }, + }, { - "created_time": "1289262856", - "text": "Little clown fish having some rest~ (Oh~~)", + "created_time": "1289262856", + "text": "Little clown fish having some rest~ (Oh~~)", "from": { - "username": "hatshepsut", - "first_name": "", - "last_name": "", - "type": "user", + "username": "hatshepsut", + "first_name": "", + "last_name": "", + "type": "user", "id": "554777" - }, + }, "id": "2500888" - }, + }, { - "created_time": "1289264482", - "text": "Beautiful!", + "created_time": "1289264482", + "text": "Beautiful!", "from": { - "username": "sandyhibbard", - "first_name": "Sandy", - "last_name": "Hibbard", - "type": "user", + "username": "sandyhibbard", + "first_name": "Sandy", + "last_name": "Hibbard", + "type": "user", "id": "110554" - }, + }, "id": "2503720" - }, + }, { - "created_time": "1289266559", - "text": "Great shot, so crisp!", + "created_time": "1289266559", + "text": "Great shot, so crisp!", "from": { - "username": "freelancefox", - "first_name": "Jonathan", - "last_name": "Burnett", - "type": "user", + "username": "freelancefox", + "first_name": "Jonathan", + "last_name": "Burnett", + "type": "user", "id": "336188" - }, + }, "id": "2507462" - }, + }, { - "created_time": "1289266699", - "text": "Great photo", + "created_time": "1289266699", + "text": "Great photo", "from": { - "username": "nervousone", - "first_name": "nervousne™", - "last_name": "", - "type": "user", + "username": "nervousone", + "first_name": "nervousne™", + "last_name": "", + "type": "user", "id": "382941" - }, + }, "id": "2507694" - }, + }, { - "created_time": "1289277163", - "text": "beautyfull!", + "created_time": "1289277163", + "text": "beautyfull!", "from": { - "username": "kasianihon", - "first_name": "kasia", - "last_name": "k", - "type": "user", + "username": "kasianihon", + "first_name": "kasia", + "last_name": "k", + "type": "user", "id": "289303" - }, + }, "id": "2526924" - }, + }, { - "created_time": "1289278235", - "text": "Good one", + "created_time": "1289278235", + "text": "Good one", "from": { - "username": "mrparinya", - "first_name": "Parinya", - "last_name": "", - "type": "user", + "username": "mrparinya", + "first_name": "Parinya", + "last_name": "", + "type": "user", "id": "414700" - }, + }, "id": "2528841" - }, + }, { - "created_time": "1289281291", - "text": "Great.", + "created_time": "1289281291", + "text": "Great.", "from": { - "username": "natchayar", - "first_name": "Natchaya", - "last_name": "Radhikulkaralak", - "type": "user", + "username": "natchayar", + "first_name": "Natchaya", + "last_name": "Radhikulkaralak", + "type": "user", "id": "148855" - }, + }, "id": "2533797" - }, + }, { - "created_time": "1289281909", - "text": "Nemo!!! Marlin is looking for you :)", + "created_time": "1289281909", + "text": "Nemo!!! Marlin is looking for you :)", "from": { - "username": "sgaddi", - "first_name": "Sheila", - "last_name": "Gaddi", - "type": "user", + "username": "sgaddi", + "first_name": "Sheila", + "last_name": "Gaddi", + "type": "user", "id": "551630" - }, + }, "id": "2534814" - }, + }, { - "created_time": "1289285109", - "text": "Wow!!! Did you really take that?", + "created_time": "1289285109", + "text": "Wow!!! Did you really take that?", "from": { - "username": "truehaitian", - "first_name": "Sir Marc", - "last_name": "", - "type": "user", + "username": "truehaitian", + "first_name": "Sir Marc", + "last_name": "", + "type": "user", "id": "548830" - }, + }, "id": "2539660" - }, + }, { - "created_time": "1289296026", - "text": "Good shot", + "created_time": "1289296026", + "text": "Good shot", "from": { - "username": "photopia", - "first_name": "Photographer", - "last_name": "Photopia", - "type": "user", + "username": "photopia", + "first_name": "Photographer", + "last_name": "Photopia", + "type": "user", "id": "196466" - }, + }, "id": "2555548" } ] - }, + }, "caption": { - "created_time": "1289256909", - "text": "Clown fish", + "created_time": "1289256909", + "text": "Clown fish", "from": { - "username": "sirhc1", - "first_name": "", - "last_name": "", - "type": "user", + "username": "sirhc1", + "first_name": "", + "last_name": "", + "type": "user", "id": "423404" - }, + }, "id": "2489109" - }, - "link": "http://localhost:8000/p/MbWd/", + }, + "link": "http://localhost:8000/p/MbWd/", "likes": { "count": 191 - }, - "created_time": "1289256881", + }, + "created_time": "1289256881", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/5797e5a189e54c3e8e891022b5b72333_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/5797e5a189e54c3e8e891022b5b72333_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/5797e5a189e54c3e8e891022b5b72333_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/5797e5a189e54c3e8e891022b5b72333_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/5797e5a189e54c3e8e891022b5b72333_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/5797e5a189e54c3e8e891022b5b72333_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3257757", + }, + "user_has_liked": false, + "id": "3257757", "user": { - "username": "sirhc1", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_423404_75sq_1288822983.jpg", + "username": "sirhc1", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_423404_75sq_1288822983.jpg", "id": "423404" } - }, + }, { - "type": "image", - "location": null, + "type": "image", + "tags": [], + "location": null, "comments": { - "count": 26, + "count": 26, "data": [ { - "created_time": "1289273295", - "text": "Tulip closeup", + "created_time": "1289273295", + "text": "Tulip closeup", "from": { - "username": "greendot", - "first_name": "Lisa", - "last_name": "S", - "type": "user", + "username": "greendot", + "first_name": "Lisa", + "last_name": "S", + "type": "user", "id": "355967" - }, + }, "id": "2519468" - }, + }, { - "created_time": "1289273408", - "text": "Very nice!!! Love this!", + "created_time": "1289273408", + "text": "Very nice!!! Love this!", "from": { - "username": "photobug300", - "first_name": "", - "last_name": "", - "type": "user", + "username": "photobug300", + "first_name": "", + "last_name": "", + "type": "user", "id": "515924" - }, + }, "id": "2519700" - }, + }, { - "created_time": "1289273569", - "text": "Que linda", + "created_time": "1289273569", + "text": "Que linda", "from": { - "username": "cyberespia", - "first_name": "Mario", - "last_name": "Burga", - "type": "user", + "username": "cyberespia", + "first_name": "Mario", + "last_name": "Burga", + "type": "user", "id": "351356" - }, + }, "id": "2520070" - }, + }, { - "created_time": "1289273701", - "text": "Beautiful !!!", + "created_time": "1289273701", + "text": "Beautiful !!!", "from": { - "username": "rubypooh", - "first_name": "", - "last_name": "", - "type": "user", + "username": "rubypooh", + "first_name": "", + "last_name": "", + "type": "user", "id": "389851" - }, + }, "id": "2520328" - }, + }, { - "created_time": "1289275304", - "text": "Good!!", + "created_time": "1289275304", + "text": "Good!!", "from": { - "username": "youngwook", - "first_name": "", - "last_name": "", - "type": "user", + "username": "youngwook", + "first_name": "", + "last_name": "", + "type": "user", "id": "416445" - }, + }, "id": "2523450" - }, + }, { - "created_time": "1289275778", - "text": "so beautiful!!", + "created_time": "1289275778", + "text": "so beautiful!!", "from": { - "username": "pagsf", - "first_name": "Pat", - "last_name": "G", - "type": "user", + "username": "pagsf", + "first_name": "Pat", + "last_name": "G", + "type": "user", "id": "86391" - }, + }, "id": "2524345" - }, + }, { - "created_time": "1289276041", - "text": "Very pretty!", + "created_time": "1289276041", + "text": "Very pretty!", "from": { - "username": "paroxysmism", - "first_name": "Please follow me", - "last_name": ":O", - "type": "user", + "username": "paroxysmism", + "first_name": "Please follow me", + "last_name": ":O", + "type": "user", "id": "553151" - }, + }, "id": "2524835" - }, + }, { - "created_time": "1289276538", - "text": "This is really pretty!", + "created_time": "1289276538", + "text": "This is really pretty!", "from": { - "username": "mandiec", - "first_name": "Mandie", - "last_name": "", - "type": "user", + "username": "mandiec", + "first_name": "Mandie", + "last_name": "", + "type": "user", "id": "563899" - }, + }, "id": "2525759" - }, + }, { - "created_time": "1289277180", - "text": "Good!!", + "created_time": "1289277180", + "text": "Good!!", "from": { - "username": "sam360", - "first_name": "osamu", - "last_name": "kato", - "type": "user", + "username": "sam360", + "first_name": "osamu", + "last_name": "kato", + "type": "user", "id": "432607" - }, + }, "id": "2526952" - }, + }, { - "created_time": "1289277289", - "text": "LOVE this", + "created_time": "1289277289", + "text": "LOVE this", "from": { - "username": "jennchhuth", - "first_name": "Jennifer", - "last_name": "Chhuth", - "type": "user", + "username": "jennchhuth", + "first_name": "Jennifer", + "last_name": "Chhuth", + "type": "user", "id": "469175" - }, + }, "id": "2527146" - }, + }, { - "created_time": "1289277578", - "text": "Beautiful!", + "created_time": "1289277578", + "text": "Beautiful!", "from": { - "username": "singyoni", - "first_name": "", - "last_name": "", - "type": "user", + "username": "singyoni", + "first_name": "", + "last_name": "", + "type": "user", "id": "493141" - }, + }, "id": "2527673" - }, + }, { - "created_time": "1289279323", - "text": "gorgeous!!", + "created_time": "1289279323", + "text": "gorgeous!!", "from": { - "username": "cameradiary", - "first_name": "", - "last_name": "", - "type": "user", + "username": "cameradiary", + "first_name": "", + "last_name": "", + "type": "user", "id": "421958" - }, + }, "id": "2530611" - }, + }, { - "created_time": "1289279994", - "text": "really good photo(^_^) 綺麗", + "created_time": "1289279994", + "text": "really good photo(^_^) 綺麗", "from": { - "username": "aki13", - "first_name": "", - "last_name": "", - "type": "user", + "username": "aki13", + "first_name": "", + "last_name": "", + "type": "user", "id": "526921" - }, + }, "id": "2531642" - }, + }, { - "created_time": "1289281767", - "text": "So well done", + "created_time": "1289281767", + "text": "So well done", "from": { - "username": "larnhill", - "first_name": "Kent", - "last_name": "Larnhill", - "type": "user", + "username": "larnhill", + "first_name": "Kent", + "last_name": "Larnhill", + "type": "user", "id": "103180" - }, + }, "id": "2534579" - }, + }, { - "created_time": "1289281858", - "text": "Stunning", + "created_time": "1289281858", + "text": "Stunning", "from": { - "username": "ochoaliz", - "first_name": "", - "last_name": "", - "type": "user", + "username": "ochoaliz", + "first_name": "", + "last_name": "", + "type": "user", "id": "549521" - }, + }, "id": "2534747" - }, + }, { - "created_time": "1289282574", - "text": "Wowowowoooo!!!! Ab fab!", + "created_time": "1289282574", + "text": "Wowowowoooo!!!! Ab fab!", "from": { - "username": "kerenw", - "first_name": "Keren", - "last_name": "W", - "type": "user", + "username": "kerenw", + "first_name": "Keren", + "last_name": "W", + "type": "user", "id": "127266" - }, + }, "id": "2535925" - }, + }, { - "created_time": "1289282981", - "text": "Stunning!", + "created_time": "1289282981", + "text": "Stunning!", "from": { - "username": "java1215", - "first_name": "Kim", - "last_name": "", - "type": "user", + "username": "java1215", + "first_name": "Kim", + "last_name": "", + "type": "user", "id": "360594" - }, + }, "id": "2536500" - }, + }, { - "created_time": "1289283647", - "text": "This is lovely. Simple and beautiful. Great shot!!", + "created_time": "1289283647", + "text": "This is lovely. Simple and beautiful. Great shot!!", "from": { - "username": "samdragoo", - "first_name": "Sam", - "last_name": "", - "type": "user", + "username": "samdragoo", + "first_name": "Sam", + "last_name": "", + "type": "user", "id": "368471" - }, + }, "id": "2537514" - }, + }, { - "created_time": "1289283937", - "text": "Beautiful :)", + "created_time": "1289283937", + "text": "Beautiful :)", "from": { - "username": "katiewalker", - "first_name": "Katie", - "last_name": "Smithey", - "type": "user", + "username": "katiewalker", + "first_name": "Katie", + "last_name": "Smithey", + "type": "user", "id": "522734" - }, + }, "id": "2537951" - }, + }, { - "created_time": "1289284211", - "text": "Wow this is awesome !!!!", + "created_time": "1289284211", + "text": "Wow this is awesome !!!!", "from": { - "username": "stjuttemus", - "first_name": "@ Loewiese", - "last_name": "", - "type": "user", + "username": "stjuttemus", + "first_name": "@ Loewiese", + "last_name": "", + "type": "user", "id": "375558" - }, + }, "id": "2538415" - }, + }, { - "created_time": "1289290437", - "text": "Fantastic", + "created_time": "1289290437", + "text": "Fantastic", "from": { - "username": "plebdeb", - "first_name": "Debbie", - "last_name": "Callway", - "type": "user", + "username": "plebdeb", + "first_name": "Debbie", + "last_name": "Callway", + "type": "user", "id": "153584" - }, + }, "id": "2547764" - }, + }, { - "created_time": "1289300285", - "text": "Fantastic shot!! I", + "created_time": "1289300285", + "text": "Fantastic shot!! I", "from": { - "username": "elwood", - "first_name": "", - "last_name": "", - "type": "user", + "username": "elwood", + "first_name": "", + "last_name": "", + "type": "user", "id": "448284" - }, + }, "id": "2562081" - }, + }, { - "created_time": "1289306046", - "text": "Beautiful!!", + "created_time": "1289306046", + "text": "Beautiful!!", "from": { - "username": "gershwin", - "first_name": "Gersh", - "last_name": "Win", - "type": "user", + "username": "gershwin", + "first_name": "Gersh", + "last_name": "Win", + "type": "user", "id": "312872" - }, + }, "id": "2572207" - }, + }, { - "created_time": "1289310762", - "text": "wow!!", + "created_time": "1289310762", + "text": "wow!!", "from": { - "username": "flyawayskywing", - "first_name": "바람", - "last_name": "_", - "type": "user", + "username": "flyawayskywing", + "first_name": "바람", + "last_name": "_", + "type": "user", "id": "188387" - }, + }, "id": "2581759" - }, + }, { - "created_time": "1289322173", - "text": "Love the soft lighting", + "created_time": "1289322173", + "text": "Love the soft lighting", "from": { - "username": "jwlsart", - "first_name": "Juliya", - "last_name": "Gudev", - "type": "user", + "username": "jwlsart", + "first_name": "Juliya", + "last_name": "Gudev", + "type": "user", "id": "178676" - }, + }, "id": "2604370" - }, + }, { - "created_time": "1289323872", - "text": "Wow", + "created_time": "1289323872", + "text": "Wow", "from": { - "username": "thespoiledbutterfly", - "first_name": "Miranda", - "last_name": "", - "type": "user", + "username": "thespoiledbutterfly", + "first_name": "Miranda", + "last_name": "", + "type": "user", "id": "417002" - }, + }, "id": "2606899" } ] - }, + }, "caption": { - "created_time": "1289273295", - "text": "Tulip closeup", + "created_time": "1289273295", + "text": "Tulip closeup", "from": { - "username": "greendot", - "first_name": "Lisa", - "last_name": "S", - "type": "user", + "username": "greendot", + "first_name": "Lisa", + "last_name": "S", + "type": "user", "id": "355967" - }, + }, "id": "2519468" - }, - "link": "http://localhost:8000/p/Mi3z/", + }, + "link": "http://localhost:8000/p/Mi3z/", "likes": { "count": 378 - }, - "created_time": "1289273285", + }, + "created_time": "1289273285", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/1ea034c51af8418b8b8db84cd9c5cbf0_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/1ea034c51af8418b8b8db84cd9c5cbf0_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/1ea034c51af8418b8b8db84cd9c5cbf0_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/1ea034c51af8418b8b8db84cd9c5cbf0_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/1ea034c51af8418b8b8db84cd9c5cbf0_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/1ea034c51af8418b8b8db84cd9c5cbf0_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3288563", + }, + "user_has_liked": false, + "id": "3288563", "user": { - "username": "greendot", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_355967_75sq_1288793234.jpg", + "username": "greendot", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_355967_75sq_1288793234.jpg", "id": "355967" } - }, + }, { - "type": "image", - "location": null, + "type": "image", + "tags": [], + "location": null, "comments": { - "count": 7, + "count": 7, "data": [ { - "created_time": "1289323752", - "text": "Luminarium", + "created_time": "1289323752", + "text": "Luminarium", "from": { - "username": "massive_department", - "first_name": "max", - "last_name": "dept", - "type": "user", + "username": "massive_department", + "first_name": "max", + "last_name": "dept", + "type": "user", "id": "418966" - }, + }, "id": "2606721" - }, + }, { - "created_time": "1289324227", - "text": "Thanks for following. Wow! I love love this image, very cool.", + "created_time": "1289324227", + "text": "Thanks for following. Wow! I love love this image, very cool.", "from": { - "username": "sandyhibbard", - "first_name": "Sandy", - "last_name": "Hibbard", - "type": "user", + "username": "sandyhibbard", + "first_name": "Sandy", + "last_name": "Hibbard", + "type": "user", "id": "110554" - }, + }, "id": "2607430" - }, + }, { - "created_time": "1289324880", - "text": "nice!", + "created_time": "1289324880", + "text": "nice!", "from": { - "username": "snowstar", - "first_name": "Yuki", - "last_name": "Daruma", - "type": "user", + "username": "snowstar", + "first_name": "Yuki", + "last_name": "Daruma", + "type": "user", "id": "192441" - }, + }, "id": "2608363" - }, + }, { - "created_time": "1289325303", - "text": "Exellent pics thanx 4 following", + "created_time": "1289325303", + "text": "Exellent pics thanx 4 following", "from": { - "username": "gatoelektriko", - "first_name": "INSTAGRAM", - "last_name": "ADICT", - "type": "user", + "username": "gatoelektriko", + "first_name": "INSTAGRAM", + "last_name": "ADICT", + "type": "user", "id": "397066" - }, + }, "id": "2609025" - }, + }, { - "created_time": "1289325350", - "text": "Cool", + "created_time": "1289325350", + "text": "Cool", "from": { - "username": "cirkeline", - "first_name": "C.", - "last_name": "Andersen", - "type": "user", + "username": "cirkeline", + "first_name": "C.", + "last_name": "Andersen", + "type": "user", "id": "352855" - }, + }, "id": "2609086" - }, + }, { - "created_time": "1289325757", - "text": "Piękne!", + "created_time": "1289325757", + "text": "Piękne!", "from": { - "username": "derlacki", - "first_name": "Lukasz", - "last_name": "Derlacki", - "type": "user", + "username": "derlacki", + "first_name": "Lukasz", + "last_name": "Derlacki", + "type": "user", "id": "165358" - }, + }, "id": "2609656" - }, + }, { - "created_time": "1289326044", - "text": "Wow!", + "created_time": "1289326044", + "text": "Wow!", "from": { - "username": "candy_stealer", - "first_name": "Angie", - "last_name": "Camacho", - "type": "user", + "username": "candy_stealer", + "first_name": "Angie", + "last_name": "Camacho", + "type": "user", "id": "129022" - }, + }, "id": "2610044" } ] - }, + }, "caption": { - "created_time": "1289323752", - "text": "Luminarium", + "created_time": "1289323752", + "text": "Luminarium", "from": { - "username": "massive_department", - "first_name": "max", - "last_name": "dept", - "type": "user", + "username": "massive_department", + "first_name": "max", + "last_name": "dept", + "type": "user", "id": "418966" - }, + }, "id": "2606721" - }, - "link": "http://localhost:8000/p/M4uH/", + }, + "link": "http://localhost:8000/p/M4uH/", "likes": { "count": 16 - }, - "created_time": "1289323733", + }, + "created_time": "1289323733", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/642faac9d321456991f1b0f18697d392_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/642faac9d321456991f1b0f18697d392_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/642faac9d321456991f1b0f18697d392_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/642faac9d321456991f1b0f18697d392_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/642faac9d321456991f1b0f18697d392_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/642faac9d321456991f1b0f18697d392_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3378055", + }, + "user_has_liked": false, + "id": "3378055", "user": { - "username": "massive_department", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_418966_75sq_1289295665.jpg", + "username": "massive_department", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_418966_75sq_1289295665.jpg", "id": "418966" } - }, + }, { - "type": "image", - "location": null, + "type": "image", + "tags": [], + "location": null, "comments": { - "count": 1, + "count": 1, "data": [ { - "created_time": "1289322588", - "text": "とある窓から。", + "created_time": "1289322588", + "text": "とある窓から。", "from": { - "username": "kumikorin", - "first_name": "kumiko", - "last_name": "rin", - "type": "user", + "username": "kumikorin", + "first_name": "kumiko", + "last_name": "rin", + "type": "user", "id": "204331" - }, + }, "id": "2605036" } ] - }, + }, "caption": { - "created_time": "1289322588", - "text": "とある窓から。", + "created_time": "1289322588", + "text": "とある窓から。", "from": { - "username": "kumikorin", - "first_name": "kumiko", - "last_name": "rin", - "type": "user", + "username": "kumikorin", + "first_name": "kumiko", + "last_name": "rin", + "type": "user", "id": "204331" - }, + }, "id": "2605036" - }, - "link": "http://localhost:8000/p/M4RJ/", + }, + "link": "http://localhost:8000/p/M4RJ/", "likes": { "count": 20 - }, - "created_time": "1289322533", + }, + "created_time": "1289322533", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/f7d414a337d9406b86cc598270f3b073_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/f7d414a337d9406b86cc598270f3b073_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/f7d414a337d9406b86cc598270f3b073_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/f7d414a337d9406b86cc598270f3b073_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/f7d414a337d9406b86cc598270f3b073_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/f7d414a337d9406b86cc598270f3b073_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3376201", + }, + "user_has_liked": false, + "id": "3376201", "user": { - "username": "kumikorin", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_204331_75sq_1287916077.jpg", + "username": "kumikorin", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_204331_75sq_1287916077.jpg", "id": "204331" } - }, + }, { - "type": "image", - "location": null, + "type": "image", + "tags": [], + "location": null, "comments": { - "count": 16, + "count": 16, "data": [ { - "created_time": "1288984838", - "text": "Glass of Wine", + "created_time": "1288984838", + "text": "Glass of Wine", "from": { - "username": "arcadia", - "first_name": "Franck", - "last_name": "Vandystadt", - "type": "user", + "username": "arcadia", + "first_name": "Franck", + "last_name": "Vandystadt", + "type": "user", "id": "464780" - }, + }, "id": "1998269" - }, + }, { - "created_time": "1288998967", - "text": "awesome!", + "created_time": "1288998967", + "text": "awesome!", "from": { - "username": "manorr", - "first_name": "Manohar", - "last_name": "Singh", - "type": "user", + "username": "manorr", + "first_name": "Manohar", + "last_name": "Singh", + "type": "user", "id": "352000" - }, + }, "id": "2017920" - }, + }, { - "created_time": "1289054872", - "text": "I could use one of those!", + "created_time": "1289054872", + "text": "I could use one of those!", "from": { - "username": "kaeleah", - "first_name": "Kaeleah", - "last_name": "Rosenfeld", - "type": "user", + "username": "kaeleah", + "first_name": "Kaeleah", + "last_name": "Rosenfeld", + "type": "user", "id": "107187" - }, + }, "id": "2117838" - }, + }, { - "created_time": "1289064487", - "text": "Thank you ;-)", + "created_time": "1289064487", + "text": "Thank you ;-)", "from": { - "username": "arcadia", - "first_name": "Franck", - "last_name": "Vandystadt", - "type": "user", + "username": "arcadia", + "first_name": "Franck", + "last_name": "Vandystadt", + "type": "user", "id": "464780" - }, + }, "id": "2138082" - }, + }, { - "created_time": "1289074556", - "text": "Original, Simple, GREAT", + "created_time": "1289074556", + "text": "Original, Simple, GREAT", "from": { - "username": "torres", - "first_name": "Manuel", - "last_name": "Francisco", - "type": "user", + "username": "torres", + "first_name": "Manuel", + "last_name": "Francisco", + "type": "user", "id": "57111" - }, + }, "id": "2154738" - }, + }, { - "created_time": "1289079059", - "text": "Ohhh yeah!", + "created_time": "1289079059", + "text": "Ohhh yeah!", "from": { - "username": "edgar_aragon", - "first_name": "Edgar", - "last_name": "Aragon", - "type": "user", + "username": "edgar_aragon", + "first_name": "Edgar", + "last_name": "Aragon", + "type": "user", "id": "452401" - }, + }, "id": "2162129" - }, + }, { - "created_time": "1289086672", - "text": "Great, classy look!", + "created_time": "1289086672", + "text": "Great, classy look!", "from": { - "username": "rjmercado", - "first_name": "", - "last_name": "", - "type": "user", + "username": "rjmercado", + "first_name": "", + "last_name": "", + "type": "user", "id": "508904" - }, + }, "id": "2175753" - }, + }, { - "created_time": "1289088105", - "text": "Excellent", + "created_time": "1289088105", + "text": "Excellent", "from": { - "username": "rsuarez", - "first_name": "Rafa", - "last_name": "Suarez", - "type": "user", + "username": "rsuarez", + "first_name": "Rafa", + "last_name": "Suarez", + "type": "user", "id": "263645" - }, + }, "id": "2178300" - }, + }, { - "created_time": "1289111164", - "text": "Thank you alls. The wine was a cool french wine, and it was served to celebrate the end of a very long week...", + "created_time": "1289111164", + "text": "Thank you alls. The wine was a cool french wine, and it was served to celebrate the end of a very long week...", "from": { - "username": "arcadia", - "first_name": "Franck", - "last_name": "Vandystadt", - "type": "user", + "username": "arcadia", + "first_name": "Franck", + "last_name": "Vandystadt", + "type": "user", "id": "464780" - }, + }, "id": "2225297" - }, + }, { - "created_time": "1289135103", - "text": "♡", + "created_time": "1289135103", + "text": "♡", "from": { - "username": "6151", - "first_name": "6151", - "last_name": "", - "type": "user", + "username": "6151", + "first_name": "6151", + "last_name": "", + "type": "user", "id": "270956" - }, + }, "id": "2272429" - }, + }, { - "created_time": "1289139047", - "text": "自分も好き♬", + "created_time": "1289139047", + "text": "自分も好き♬", "from": { - "username": "koropapa", - "first_name": "", - "last_name": "", - "type": "user", + "username": "koropapa", + "first_name": "", + "last_name": "", + "type": "user", "id": "379014" - }, + }, "id": "2281436" - }, + }, { - "created_time": "1289166513", - "text": "Yum, I can almost taste it;)", + "created_time": "1289166513", + "text": "Yum, I can almost taste it;)", "from": { - "username": "lurrvvxx", - "first_name": "Abi", - "last_name": "", - "type": "user", + "username": "lurrvvxx", + "first_name": "Abi", + "last_name": "", + "type": "user", "id": "503970" - }, + }, "id": "2334106" - }, + }, { - "created_time": "1289221065", - "text": "Clasic color, so nice", + "created_time": "1289221065", + "text": "Clasic color, so nice", "from": { - "username": "freedombreeze", - "first_name": "Jay ", - "last_name": "Sritus", - "type": "user", + "username": "freedombreeze", + "first_name": "Jay ", + "last_name": "Sritus", + "type": "user", "id": "52257" - }, + }, "id": "2426107" - }, + }, { - "created_time": "1289225121", - "text": "Thank you", + "created_time": "1289225121", + "text": "Thank you", "from": { - "username": "arcadia", - "first_name": "Franck", - "last_name": "Vandystadt", - "type": "user", + "username": "arcadia", + "first_name": "Franck", + "last_name": "Vandystadt", + "type": "user", "id": "464780" - }, + }, "id": "2434497" - }, + }, { - "created_time": "1289261672", - "text": "Wine passion ;)", + "created_time": "1289261672", + "text": "Wine passion ;)", "from": { - "username": "orisend", - "first_name": "Gianluca", - "last_name": "F.", - "type": "user", + "username": "orisend", + "first_name": "Gianluca", + "last_name": "F.", + "type": "user", "id": "399748" - }, + }, "id": "2498752" - }, + }, { - "created_time": "1289325765", - "text": "Vino :)", + "created_time": "1289325765", + "text": "Vino :)", "from": { - "username": "christy07", - "first_name": "Christina", - "last_name": "Caldera", - "type": "user", + "username": "christy07", + "first_name": "Christina", + "last_name": "Caldera", + "type": "user", "id": "93740" - }, + }, "id": "2609666" } ] - }, + }, "caption": { - "created_time": "1288984838", - "text": "Glass of Wine", + "created_time": "1288984838", + "text": "Glass of Wine", "from": { - "username": "arcadia", - "first_name": "Franck", - "last_name": "Vandystadt", - "type": "user", + "username": "arcadia", + "first_name": "Franck", + "last_name": "Vandystadt", + "type": "user", "id": "464780" - }, + }, "id": "1998269" - }, - "link": "http://localhost:8000/p/KNcf/", + }, + "link": "http://localhost:8000/p/KNcf/", "likes": { "count": 88 - }, - "created_time": "1288984808", + }, + "created_time": "1288984808", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/05/481b6ecb1f324ab5b486aef444ca59dc_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/05/481b6ecb1f324ab5b486aef444ca59dc_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/05/481b6ecb1f324ab5b486aef444ca59dc_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/05/481b6ecb1f324ab5b486aef444ca59dc_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/05/481b6ecb1f324ab5b486aef444ca59dc_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/05/481b6ecb1f324ab5b486aef444ca59dc_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "2676511", + }, + "user_has_liked": false, + "id": "2676511", "user": { - "username": "arcadia", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_464780_75sq_1289014799.jpg", + "username": "arcadia", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_464780_75sq_1289014799.jpg", "id": "464780" } - }, + }, { - "type": "image", + "type": "image", + "tags": [], "location": { - "latitude": 43.060461999999987, - "id": "44692", - "longitude": 141.34739400000001, + "latitude": 43.060461999999987, + "id": "44692", + "longitude": 141.34739400000001, "name": "大通公園7丁目" - }, + }, "comments": { - "count": 1, + "count": 1, "data": [ { - "created_time": "1289102163", - "text": "Beautiful colors!", + "created_time": "1289102163", + "text": "Beautiful colors!", "from": { - "username": "littleexceptions", - "first_name": "Heaba", - "last_name": "", - "type": "user", + "username": "littleexceptions", + "first_name": "Heaba", + "last_name": "", + "type": "user", "id": "334307" - }, + }, "id": "2207034" } ] - }, - "caption": null, - "link": "http://localhost:8000/p/LNbY/", + }, + "caption": null, + "link": "http://localhost:8000/p/LNbY/", "likes": { "count": 40 - }, - "created_time": "1289102091", + }, + "created_time": "1289102091", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/06/11bcf6c70233432c885e908f6868f459_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/06/11bcf6c70233432c885e908f6868f459_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/06/11bcf6c70233432c885e908f6868f459_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/06/11bcf6c70233432c885e908f6868f459_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/06/11bcf6c70233432c885e908f6868f459_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/06/11bcf6c70233432c885e908f6868f459_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "2938584", + }, + "user_has_liked": false, + "id": "2938584", "user": { - "username": "kensuketanaka", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_156940_75sq_1289320784.jpg", + "username": "kensuketanaka", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_156940_75sq_1289320784.jpg", "id": "156940" } - }, + }, { - "type": "image", + "type": "image", + "tags": [], "location": { - "latitude": null, - "id": "12354", - "longitude": null, + "latitude": null, + "id": "12354", + "longitude": null, "name": "At Home" - }, + }, "comments": { - "count": 0, + "count": 0, "data": [] - }, - "caption": null, - "link": "http://localhost:8000/p/M41o/", + }, + "caption": null, + "link": "http://localhost:8000/p/M41o/", "likes": { "count": 19 - }, - "created_time": "1289324035", + }, + "created_time": "1289324035", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/3a5095dbd99849a598ed524adfad9939_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/3a5095dbd99849a598ed524adfad9939_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/3a5095dbd99849a598ed524adfad9939_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/3a5095dbd99849a598ed524adfad9939_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/3a5095dbd99849a598ed524adfad9939_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/3a5095dbd99849a598ed524adfad9939_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3378536", + }, + "user_has_liked": false, + "id": "3378536", "user": { - "username": "jmlares", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_46329_75sq_1286642189.jpg", + "username": "jmlares", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_46329_75sq_1286642189.jpg", "id": "46329" } - }, + }, { - "type": "image", - "location": null, + "type": "image", + "tags": [], + "location": null, "comments": { - "count": 2, + "count": 2, "data": [ { - "created_time": "1289323552", - "text": "Wonderful frame, lovely puc, love the simplicity!!", + "created_time": "1289323552", + "text": "Wonderful frame, lovely puc, love the simplicity!!", "from": { - "username": "neo121", - "first_name": "Evy", - "last_name": "Olivia", - "type": "user", + "username": "neo121", + "first_name": "Evy", + "last_name": "Olivia", + "type": "user", "id": "4124" - }, + }, "id": "2606414" - }, + }, { - "created_time": "1289323562", - "text": "I mean pic.. ;))", + "created_time": "1289323562", + "text": "I mean pic.. ;))", "from": { - "username": "neo121", - "first_name": "Evy", - "last_name": "Olivia", - "type": "user", + "username": "neo121", + "first_name": "Evy", + "last_name": "Olivia", + "type": "user", "id": "4124" - }, + }, "id": "2606425" } ] - }, - "caption": null, - "link": "http://localhost:8000/p/M4dB/", + }, + "caption": null, + "link": "http://localhost:8000/p/M4dB/", "likes": { "count": 22 - }, - "created_time": "1289323012", + }, + "created_time": "1289323012", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/038fe70c13ea444581e8cfa78689dcc5_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/038fe70c13ea444581e8cfa78689dcc5_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/038fe70c13ea444581e8cfa78689dcc5_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/038fe70c13ea444581e8cfa78689dcc5_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/038fe70c13ea444581e8cfa78689dcc5_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/038fe70c13ea444581e8cfa78689dcc5_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3376961", + }, + "user_has_liked": false, + "id": "3376961", "user": { - "username": "markowen", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_65962_75sq_1287336486.jpg", + "username": "markowen", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_65962_75sq_1287336486.jpg", "id": "65962" } - }, + }, { - "type": "image", - "location": null, + "type": "image", + "tags": [], + "location": null, "comments": { - "count": 16, + "count": 16, "data": [ { - "created_time": "1289256911", - "text": "STRIDA at Higashihama", + "created_time": "1289256911", + "text": "STRIDA at Higashihama", "from": { - "username": "taku056", - "first_name": "Taku", - "last_name": "Koike", - "type": "user", + "username": "taku056", + "first_name": "Taku", + "last_name": "Koike", + "type": "user", "id": "102076" - }, + }, "id": "2489116" - }, + }, { - "created_time": "1289266401", - "text": "Looks like fun", + "created_time": "1289266401", + "text": "Looks like fun", "from": { - "username": "kkatie", - "first_name": "Katie", - "last_name": "Rogers", - "type": "user", + "username": "kkatie", + "first_name": "Katie", + "last_name": "Rogers", + "type": "user", "id": "113713" - }, + }, "id": "2507171" - }, + }, { - "created_time": "1289266950", - "text": "i have white one ! : )", + "created_time": "1289266950", + "text": "i have white one ! : )", "from": { - "username": "zene", - "first_name": "", - "last_name": "", - "type": "user", + "username": "zene", + "first_name": "", + "last_name": "", + "type": "user", "id": "566886" - }, + }, "id": "2508132" - }, + }, { - "created_time": "1289267224", - "text": "Love the shadows and the lighting!!", + "created_time": "1289267224", + "text": "Love the shadows and the lighting!!", "from": { - "username": "aokgreen", - "first_name": "Christine", - "last_name": "", - "type": "user", + "username": "aokgreen", + "first_name": "Christine", + "last_name": "", + "type": "user", "id": "470781" - }, + }, "id": "2508625" - }, + }, { - "created_time": "1289268609", - "text": "Great picture", + "created_time": "1289268609", + "text": "Great picture", "from": { - "username": "marshall_dillon", - "first_name": "", - "last_name": "", - "type": "user", + "username": "marshall_dillon", + "first_name": "", + "last_name": "", + "type": "user", "id": "495878" - }, + }, "id": "2511071" - }, + }, { - "created_time": "1289269476", - "text": "Nicely done :)", + "created_time": "1289269476", + "text": "Nicely done :)", "from": { - "username": "senally", - "first_name": "Sen", - "last_name": "Rodriguez", - "type": "user", + "username": "senally", + "first_name": "Sen", + "last_name": "Rodriguez", + "type": "user", "id": "498487" - }, + }, "id": "2512584" - }, + }, { - "created_time": "1289269902", - "text": "Feeling this! Quality photo :-)", + "created_time": "1289269902", + "text": "Feeling this! Quality photo :-)", "from": { - "username": "crockerd", - "first_name": "Dean", - "last_name": "Crocker®", - "type": "user", + "username": "crockerd", + "first_name": "Dean", + "last_name": "Crocker®", + "type": "user", "id": "545681" - }, + }, "id": "2513357" - }, + }, { - "created_time": "1289269944", - "text": "サイクリングして~ズラね~", + "created_time": "1289269944", + "text": "サイクリングして~ズラね~", "from": { - "username": "kazuzo", - "first_name": "Tj", - "last_name": "Kazuzo", - "type": "user", + "username": "kazuzo", + "first_name": "Tj", + "last_name": "Kazuzo", + "type": "user", "id": "228224" - }, + }, "id": "2513422" - }, + }, { - "created_time": "1289273496", - "text": "Feel good!!!", + "created_time": "1289273496", + "text": "Feel good!!!", "from": { - "username": "rubypooh", - "first_name": "", - "last_name": "", - "type": "user", + "username": "rubypooh", + "first_name": "", + "last_name": "", + "type": "user", "id": "389851" - }, + }, "id": "2519907" - }, + }, { - "created_time": "1289273805", - "text": "フォローありがとうございます。湘南大好きです。", + "created_time": "1289273805", + "text": "フォローありがとうございます。湘南大好きです。", "from": { - "username": "4614works", - "first_name": "shiraishi", - "last_name": "tomohisa", - "type": "user", + "username": "4614works", + "first_name": "shiraishi", + "last_name": "tomohisa", + "type": "user", "id": "257137" - }, + }, "id": "2520548" - }, + }, { - "created_time": "1289278228", - "text": "出かけたくなる作品 (⺤‿⺤) ♬★♬★", + "created_time": "1289278228", + "text": "出かけたくなる作品 (⺤‿⺤) ♬★♬★", "from": { - "username": "001halno", - "first_name": "Halno", - "last_name": "Kujira", - "type": "user", + "username": "001halno", + "first_name": "Halno", + "last_name": "Kujira", + "type": "user", "id": "187383" - }, + }, "id": "2528829" - }, + }, { - "created_time": "1289279182", - "text": "Cool!!", + "created_time": "1289279182", + "text": "Cool!!", "from": { - "username": "sam360", - "first_name": "osamu", - "last_name": "kato", - "type": "user", + "username": "sam360", + "first_name": "osamu", + "last_name": "kato", + "type": "user", "id": "432607" - }, + }, "id": "2530401" - }, + }, { - "created_time": "1289305304", - "text": "フォローありがとうございます(^^)", + "created_time": "1289305304", + "text": "フォローありがとうございます(^^)", "from": { - "username": "amn25", - "first_name": "", - "last_name": "", - "type": "user", + "username": "amn25", + "first_name": "", + "last_name": "", + "type": "user", "id": "521532" - }, + }, "id": "2570890" - }, + }, { - "created_time": "1289313899", - "text": "フォローありがとうございます。自転車と海がマッチして素敵ですね。いい写真だ!", + "created_time": "1289313899", + "text": "フォローありがとうございます。自転車と海がマッチして素敵ですね。いい写真だ!", "from": { - "username": "nzk_0611", - "first_name": "", - "last_name": "", - "type": "user", + "username": "nzk_0611", + "first_name": "", + "last_name": "", + "type": "user", "id": "506002" - }, + }, "id": "2588369" - }, + }, { - "created_time": "1289317875", - "text": "STRIDAってフォトジェニックですねー。", + "created_time": "1289317875", + "text": "STRIDAってフォトジェニックですねー。", "from": { - "username": "sakon310", - "first_name": "Nobuaki", - "last_name": "SAKO", - "type": "user", + "username": "sakon310", + "first_name": "Nobuaki", + "last_name": "SAKO", + "type": "user", "id": "298973" - }, + }, "id": "2596613" - }, + }, { - "created_time": "1289318324", - "text": "Cool poc & strida", + "created_time": "1289318324", + "text": "Cool poc & strida", "from": { - "username": "wassup", - "first_name": "K-Sub", - "last_name": "Shin", - "type": "user", + "username": "wassup", + "first_name": "K-Sub", + "last_name": "Shin", + "type": "user", "id": "436150" - }, + }, "id": "2597465" } ] - }, + }, "caption": { - "created_time": "1289256911", - "text": "STRIDA at Higashihama", + "created_time": "1289256911", + "text": "STRIDA at Higashihama", "from": { - "username": "taku056", - "first_name": "Taku", - "last_name": "Koike", - "type": "user", + "username": "taku056", + "first_name": "Taku", + "last_name": "Koike", + "type": "user", "id": "102076" - }, + }, "id": "2489116" - }, - "link": "http://localhost:8000/p/MbW6/", + }, + "link": "http://localhost:8000/p/MbW6/", "likes": { "count": 202 - }, - "created_time": "1289256895", + }, + "created_time": "1289256895", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/934f407aad9b47d59f0b44abae958d0a_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/934f407aad9b47d59f0b44abae958d0a_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/934f407aad9b47d59f0b44abae958d0a_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/934f407aad9b47d59f0b44abae958d0a_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/934f407aad9b47d59f0b44abae958d0a_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/934f407aad9b47d59f0b44abae958d0a_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3257786", + }, + "user_has_liked": false, + "id": "3257786", "user": { - "username": "taku056", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_102076_75sq_1288478795.jpg", + "username": "taku056", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_102076_75sq_1288478795.jpg", "id": "102076" } - }, + }, { - "type": "image", + "type": "image", + "tags": [], "location": { - "latitude": 33.640747599999997, - "id": "249875", - "longitude": -84.438301900000013, + "latitude": 33.640747599999997, + "id": "249875", + "longitude": -84.438301900000013, "name": "Delta Flight 2629" - }, + }, "comments": { - "count": 11, + "count": 11, "data": [ { - "created_time": "1289323336", - "text": "If you were 7' 2\"", + "created_time": "1289323336", + "text": "If you were 7' 2\"", "from": { - "username": "joshjohnson", - "first_name": "", - "last_name": "INSTATIPS", - "type": "user", + "username": "joshjohnson", + "first_name": "", + "last_name": "INSTATIPS", + "type": "user", "id": "98476" - }, + }, "id": "2606131" - }, + }, { - "created_time": "1289323527", - "text": "Josh, now this is deeeeep! ;). Nice shot", + "created_time": "1289323527", + "text": "Josh, now this is deeeeep! ;). Nice shot", "from": { - "username": "dvaughnluma", - "first_name": "Dustin", - "last_name": "Vaughn-Luma", - "type": "user", + "username": "dvaughnluma", + "first_name": "Dustin", + "last_name": "Vaughn-Luma", + "type": "user", "id": "235126" - }, + }, "id": "2606376" - }, + }, { - "created_time": "1289323696", - "text": "Very cool shot and processing, well done", + "created_time": "1289323696", + "text": "Very cool shot and processing, well done", "from": { - "username": "andyl", - "first_name": "Andy", - "last_name": "Lovegrove", - "type": "user", + "username": "andyl", + "first_name": "Andy", + "last_name": "Lovegrove", + "type": "user", "id": "274313" - }, + }, "id": "2606638" - }, + }, { - "created_time": "1289323978", - "text": "Great angle and shot", + "created_time": "1289323978", + "text": "Great angle and shot", "from": { - "username": "sanjayprasad", - "first_name": "Sanjay", - "last_name": "Prasad", - "type": "user", + "username": "sanjayprasad", + "first_name": "Sanjay", + "last_name": "Prasad", + "type": "user", "id": "244949" - }, + }, "id": "2607058" - }, + }, { - "created_time": "1289324377", - "text": "Nice angle!", + "created_time": "1289324377", + "text": "Nice angle!", "from": { - "username": "yuzucha", - "first_name": "❇yurico❇", - "last_name": "", - "type": "user", + "username": "yuzucha", + "first_name": "❇yurico❇", + "last_name": "", + "type": "user", "id": "258807" - }, + }, "id": "2607651" - }, + }, { - "created_time": "1289324416", - "text": "Great comment :D Like your shot!", + "created_time": "1289324416", + "text": "Great comment :D Like your shot!", "from": { - "username": "loooovedesign", - "first_name": "Anette", - "last_name": "Joost", - "type": "user", + "username": "loooovedesign", + "first_name": "Anette", + "last_name": "Joost", + "type": "user", "id": "107210" - }, + }, "id": "2607710" - }, + }, { - "created_time": "1289324499", - "text": "Neat.", + "created_time": "1289324499", + "text": "Neat.", "from": { - "username": "mayoff", - "first_name": "Jason", - "last_name": "Mayoff", - "type": "user", + "username": "mayoff", + "first_name": "Jason", + "last_name": "Mayoff", + "type": "user", "id": "372623" - }, + }, "id": "2607814" - }, + }, { - "created_time": "1289324578", - "text": "Coooool", + "created_time": "1289324578", + "text": "Coooool", "from": { - "username": "lechat8", - "first_name": "Shino", - "last_name": "", - "type": "user", + "username": "lechat8", + "first_name": "Shino", + "last_name": "", + "type": "user", "id": "288248" - }, + }, "id": "2607914" - }, + }, { - "created_time": "1289324598", - "text": "Cool pic.", + "created_time": "1289324598", + "text": "Cool pic.", "from": { - "username": "roguestatus", - "first_name": "Shane", - "last_name": "", - "type": "user", + "username": "roguestatus", + "first_name": "Shane", + "last_name": "", + "type": "user", "id": "427701" - }, + }, "id": "2607944" - }, + }, { - "created_time": "1289325297", - "text": "This one is a killer! Can wait to see all your travel pics", + "created_time": "1289325297", + "text": "This one is a killer! Can wait to see all your travel pics", "from": { - "username": "baruchin", - "first_name": "Barusch", - "last_name": "Benitez", - "type": "user", + "username": "baruchin", + "first_name": "Barusch", + "last_name": "Benitez", + "type": "user", "id": "322291" - }, + }, "id": "2609013" - }, + }, { - "created_time": "1289326187", - "text": "Nailed it again fella", + "created_time": "1289326187", + "text": "Nailed it again fella", "from": { - "username": "socobloke", - "first_name": "Scott", - "last_name": "Johnston", - "type": "user", + "username": "socobloke", + "first_name": "Scott", + "last_name": "Johnston", + "type": "user", "id": "36488" - }, + }, "id": "2610210" } ] - }, + }, "caption": { - "created_time": "1289323336", - "text": "If you were 7' 2\"", + "created_time": "1289323336", + "text": "If you were 7' 2\"", "from": { - "username": "joshjohnson", - "first_name": "", - "last_name": "INSTATIPS", - "type": "user", + "username": "joshjohnson", + "first_name": "", + "last_name": "INSTATIPS", + "type": "user", "id": "98476" - }, + }, "id": "2606131" - }, - "link": "http://localhost:8000/p/M4kJ/", + }, + "link": "http://localhost:8000/p/M4kJ/", "likes": { "count": 103 - }, - "created_time": "1289323312", + }, + "created_time": "1289323312", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/94889516139341e08ac25b46feba87b7_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/94889516139341e08ac25b46feba87b7_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/94889516139341e08ac25b46feba87b7_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/94889516139341e08ac25b46feba87b7_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/94889516139341e08ac25b46feba87b7_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/94889516139341e08ac25b46feba87b7_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3377417", + }, + "user_has_liked": false, + "id": "3377417", "user": { - "username": "joshjohnson", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_98476_75sq_1288321499.jpg", + "username": "joshjohnson", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_98476_75sq_1288321499.jpg", "id": "98476" } - }, + }, { - "type": "image", + "type": "image", + "tags": [], "location": { - "latitude": 40.724641499999997, - "id": "241706", - "longitude": -73.994217000000006, + "latitude": 40.724641499999997, + "id": "241706", + "longitude": -73.994217000000006, "name": "Caribbean Deli" - }, + }, "comments": { - "count": 2, + "count": 2, "data": [ { - "created_time": "1289249590", - "text": "Soho #NYC ", + "created_time": "1289249590", + "text": "Soho #NYC ", "from": { - "username": "angelceballos", - "first_name": "Angel", - "last_name": "Ceballos", - "type": "user", + "username": "angelceballos", + "first_name": "Angel", + "last_name": "Ceballos", + "type": "user", "id": "499824" - }, + }, "id": "2475591" - }, + }, { - "created_time": "1289267293", - "text": "I like there", + "created_time": "1289267293", + "text": "I like there", "from": { - "username": "bustedtime", - "first_name": "", - "last_name": "", - "type": "user", + "username": "bustedtime", + "first_name": "", + "last_name": "", + "type": "user", "id": "415030" - }, + }, "id": "2508737" } ] - }, + }, "caption": { - "created_time": "1289249590", - "text": "Soho #NYC ", + "created_time": "1289249590", + "text": "Soho #NYC ", "from": { - "username": "angelceballos", - "first_name": "Angel", - "last_name": "Ceballos", - "type": "user", + "username": "angelceballos", + "first_name": "Angel", + "last_name": "Ceballos", + "type": "user", "id": "499824" - }, + }, "id": "2475591" - }, - "link": "http://localhost:8000/p/MYCZ/", + }, + "link": "http://localhost:8000/p/MYCZ/", "likes": { "count": 40 - }, - "created_time": "1289249588", + }, + "created_time": "1289249588", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/4d73d6e1ed834d868a85bf2200381739_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/4d73d6e1ed834d868a85bf2200381739_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/4d73d6e1ed834d868a85bf2200381739_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/4d73d6e1ed834d868a85bf2200381739_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/4d73d6e1ed834d868a85bf2200381739_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/4d73d6e1ed834d868a85bf2200381739_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3244185", + }, + "user_has_liked": false, + "id": "3244185", "user": { - "username": "angelceballos", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_499824_75sq_1289028355.jpg", + "username": "angelceballos", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_499824_75sq_1289028355.jpg", "id": "499824" } - }, + }, { - "type": "image", + "type": "image", + "tags": [], "location": { - "latitude": null, - "id": "106064", - "longitude": null, + "latitude": null, + "id": "106064", + "longitude": null, "name": "Southville" - }, + }, "comments": { - "count": 17, + "count": 17, "data": [ { - "created_time": "1289259696", - "text": "The Tree, iPhone, Straight, Filter Storm, Photogene.", + "created_time": "1289259696", + "text": "The Tree, iPhone, Straight, Filter Storm, Photogene.", "from": { - "username": "andyl", - "first_name": "Andy", - "last_name": "Lovegrove", - "type": "user", + "username": "andyl", + "first_name": "Andy", + "last_name": "Lovegrove", + "type": "user", "id": "274313" - }, + }, "id": "2495079" - }, + }, { - "created_time": "1289259745", - "text": "Wow love it! A path of light", + "created_time": "1289259745", + "text": "Wow love it! A path of light", "from": { - "username": "frosty1", - "first_name": "Frosty", - "last_name": "Frosty", - "type": "user", + "username": "frosty1", + "first_name": "Frosty", + "last_name": "Frosty", + "type": "user", "id": "55884" - }, + }, "id": "2495176" - }, + }, { - "created_time": "1289261879", - "text": "Looks good. I wish the iPod touch 4G had more filters and a better camera.", + "created_time": "1289261879", + "text": "Looks good. I wish the iPod touch 4G had more filters and a better camera.", "from": { - "username": "silverfox", - "first_name": "", - "last_name": "", - "type": "user", + "username": "silverfox", + "first_name": "", + "last_name": "", + "type": "user", "id": "346692" - }, + }, "id": "2499157" - }, + }, { - "created_time": "1289264160", - "text": "Beautiful colors", + "created_time": "1289264160", + "text": "Beautiful colors", "from": { - "username": "pri_br", - "first_name": "...Priscila", - "last_name": "Bezerra", - "type": "user", + "username": "pri_br", + "first_name": "...Priscila", + "last_name": "Bezerra", + "type": "user", "id": "512399" - }, + }, "id": "2503182" - }, + }, { - "created_time": "1289265679", - "text": "Nice symmetry yet variety.", + "created_time": "1289265679", + "text": "Nice symmetry yet variety.", "from": { - "username": "splinteredmind", - "first_name": "D.", - "last_name": "Cootey", - "type": "user", + "username": "splinteredmind", + "first_name": "D.", + "last_name": "Cootey", + "type": "user", "id": "32196" - }, + }, "id": "2505883" - }, + }, { - "created_time": "1289269593", - "text": "This is so so so wonderful!!!", + "created_time": "1289269593", + "text": "This is so so so wonderful!!!", "from": { - "username": "brittneyinazkaban", - "first_name": "Brittney", - "last_name": "Shepherd", - "type": "user", + "username": "brittneyinazkaban", + "first_name": "Brittney", + "last_name": "Shepherd", + "type": "user", "id": "85933" - }, + }, "id": "2512811" - }, + }, { - "created_time": "1289270287", - "text": "Beautiful shot - glad to hear iPhone. Thx for app info!", + "created_time": "1289270287", + "text": "Beautiful shot - glad to hear iPhone. Thx for app info!", "from": { - "username": "alideer", - "first_name": "", - "last_name": "", - "type": "user", + "username": "alideer", + "first_name": "", + "last_name": "", + "type": "user", "id": "368120" - }, + }, "id": "2514022" - }, + }, { - "created_time": "1289270599", - "text": "Love it!!", + "created_time": "1289270599", + "text": "Love it!!", "from": { - "username": "bella_riv", - "first_name": "Bella", - "last_name": "Riv", - "type": "user", + "username": "bella_riv", + "first_name": "Bella", + "last_name": "Riv", + "type": "user", "id": "169720" - }, + }, "id": "2514562" - }, + }, { - "created_time": "1289270649", - "text": "Great pic, check out the pic of the spider I just took", + "created_time": "1289270649", + "text": "Great pic, check out the pic of the spider I just took", "from": { - "username": "beachmatt", - "first_name": "Matt", - "last_name": "Gilbert", - "type": "user", + "username": "beachmatt", + "first_name": "Matt", + "last_name": "Gilbert", + "type": "user", "id": "334820" - }, + }, "id": "2514642" - }, + }, { - "created_time": "1289271067", - "text": "Great shot", + "created_time": "1289271067", + "text": "Great shot", "from": { - "username": "jcasebolt", - "first_name": "Jon", - "last_name": "Casebolt", - "type": "user", + "username": "jcasebolt", + "first_name": "Jon", + "last_name": "Casebolt", + "type": "user", "id": "546540" - }, + }, "id": "2515364" - }, + }, { - "created_time": "1289271893", - "text": "This picture is beautiful", + "created_time": "1289271893", + "text": "This picture is beautiful", "from": { - "username": "cbeatz852", - "first_name": "Chris", - "last_name": "Robinson", - "type": "user", + "username": "cbeatz852", + "first_name": "Chris", + "last_name": "Robinson", + "type": "user", "id": "487794" - }, + }, "id": "2516772" - }, + }, { - "created_time": "1289272933", - "text": "Very beautiful capture. I have sone new fall shots up for everyone.", + "created_time": "1289272933", + "text": "Very beautiful capture. I have sone new fall shots up for everyone.", "from": { - "username": "elitehaxor", - "first_name": "William", - "last_name": "Griffin", - "type": "user", + "username": "elitehaxor", + "first_name": "William", + "last_name": "Griffin", + "type": "user", "id": "512543" - }, + }, "id": "2518735" - }, + }, { - "created_time": "1289274784", - "text": "멋져요~^^", + "created_time": "1289274784", + "text": "멋져요~^^", "from": { - "username": "ansrllee", - "first_name": "", - "last_name": "", - "type": "user", + "username": "ansrllee", + "first_name": "", + "last_name": "", + "type": "user", "id": "568368" - }, + }, "id": "2522463" - }, + }, { - "created_time": "1289280373", - "text": "Beautiful I absolutely love it :)", + "created_time": "1289280373", + "text": "Beautiful I absolutely love it :)", "from": { - "username": "ladixluck", - "first_name": "Chris", - "last_name": "", - "type": "user", + "username": "ladixluck", + "first_name": "Chris", + "last_name": "", + "type": "user", "id": "563748" - }, + }, "id": "2532238" - }, + }, { - "created_time": "1289314587", - "text": "Amazing shot, absolutely love it", + "created_time": "1289314587", + "text": "Amazing shot, absolutely love it", "from": { - "username": "shayehill", - "first_name": "Shaye", - "last_name": "Hill", - "type": "user", + "username": "shayehill", + "first_name": "Shaye", + "last_name": "Hill", + "type": "user", "id": "190149" - }, + }, "id": "2589825" - }, + }, { - "created_time": "1289319818", - "text": "very nice!!!", + "created_time": "1289319818", + "text": "very nice!!!", "from": { - "username": "manorr", - "first_name": "Manohar", - "last_name": "Singh", - "type": "user", + "username": "manorr", + "first_name": "Manohar", + "last_name": "Singh", + "type": "user", "id": "352000" - }, + }, "id": "2600204" - }, + }, { - "created_time": "1289320423", - "text": "Beautiful", + "created_time": "1289320423", + "text": "Beautiful", "from": { - "username": "micryu", - "first_name": "Tatsuya", - "last_name": "Higashi", - "type": "user", + "username": "micryu", + "first_name": "Tatsuya", + "last_name": "Higashi", + "type": "user", "id": "214189" - }, + }, "id": "2601417" } ] - }, + }, "caption": { - "created_time": "1289259696", - "text": "The Tree, iPhone, Straight, Filter Storm, Photogene.", + "created_time": "1289259696", + "text": "The Tree, iPhone, Straight, Filter Storm, Photogene.", "from": { - "username": "andyl", - "first_name": "Andy", - "last_name": "Lovegrove", - "type": "user", + "username": "andyl", + "first_name": "Andy", + "last_name": "Lovegrove", + "type": "user", "id": "274313" - }, + }, "id": "2495079" - }, - "link": "http://localhost:8000/p/McrH/", + }, + "link": "http://localhost:8000/p/McrH/", "likes": { "count": 161 - }, - "created_time": "1289259595", + }, + "created_time": "1289259595", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/e47fc11f7f0447ee93099a6c8f7cf5d8_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/e47fc11f7f0447ee93099a6c8f7cf5d8_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/e47fc11f7f0447ee93099a6c8f7cf5d8_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/e47fc11f7f0447ee93099a6c8f7cf5d8_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/e47fc11f7f0447ee93099a6c8f7cf5d8_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/08/e47fc11f7f0447ee93099a6c8f7cf5d8_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3263175", + }, + "user_has_liked": false, + "id": "3263175", "user": { - "username": "andyl", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_274313_75sq_1288169298.jpg", + "username": "andyl", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_274313_75sq_1288169298.jpg", "id": "274313" } - }, + }, { - "type": "image", - "location": null, + "type": "image", + "tags": [], + "location": null, "comments": { - "count": 0, + "count": 0, "data": [] - }, - "caption": null, - "link": "http://localhost:8000/p/KZ-H/", + }, + "caption": null, + "link": "http://localhost:8000/p/KZ-H/", "likes": { "count": 71 - }, - "created_time": "1289011900", + }, + "created_time": "1289011900", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/05/ed1f82beaa2845e6b9799f4eb13b7b2a_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/05/ed1f82beaa2845e6b9799f4eb13b7b2a_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/05/ed1f82beaa2845e6b9799f4eb13b7b2a_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/05/ed1f82beaa2845e6b9799f4eb13b7b2a_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/05/ed1f82beaa2845e6b9799f4eb13b7b2a_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/05/ed1f82beaa2845e6b9799f4eb13b7b2a_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "2727815", + }, + "user_has_liked": false, + "id": "2727815", "user": { - "username": "aaronzhao", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_161521_75sq_1288331111.jpg", + "username": "aaronzhao", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_161521_75sq_1288331111.jpg", "id": "161521" } - }, + }, { - "type": "image", - "location": null, + "type": "image", + "tags": [], + "location": null, "comments": { - "count": 5, + "count": 5, "data": [ { - "created_time": "1289323345", - "text": "Flowers.", + "created_time": "1289323345", + "text": "Flowers.", "from": { - "username": "mxwife19", - "first_name": " Motocross Wife ", - "last_name": "", - "type": "user", + "username": "mxwife19", + "first_name": " Motocross Wife ", + "last_name": "", + "type": "user", "id": "486164" - }, + }, "id": "2606139" - }, + }, { - "created_time": "1289324023", - "text": "Real nice", + "created_time": "1289324023", + "text": "Real nice", "from": { - "username": "dodgerdan", - "first_name": "", - "last_name": "", - "type": "user", + "username": "dodgerdan", + "first_name": "", + "last_name": "", + "type": "user", "id": "465604" - }, + }, "id": "2607110" - }, + }, { - "created_time": "1289324197", - "text": "My moms house. Lol ;)", + "created_time": "1289324197", + "text": "My moms house. Lol ;)", "from": { - "username": "mxwife19", - "first_name": " Motocross Wife ", - "last_name": "", - "type": "user", + "username": "mxwife19", + "first_name": " Motocross Wife ", + "last_name": "", + "type": "user", "id": "486164" - }, + }, "id": "2607377" - }, + }, { - "created_time": "1289324467", - "text": "This is a great picture. Thanks for posting it.", + "created_time": "1289324467", + "text": "This is a great picture. Thanks for posting it.", "from": { - "username": "mayoff", - "first_name": "Jason", - "last_name": "Mayoff", - "type": "user", + "username": "mayoff", + "first_name": "Jason", + "last_name": "Mayoff", + "type": "user", "id": "372623" - }, + }, "id": "2607774" - }, + }, { - "created_time": "1289325276", - "text": "NiceColors´◡`♪", + "created_time": "1289325276", + "text": "NiceColors´◡`♪", "from": { - "username": "kyooon", - "first_name": "Kyon", - "last_name": "M", - "type": "user", + "username": "kyooon", + "first_name": "Kyon", + "last_name": "M", + "type": "user", "id": "274447" - }, + }, "id": "2608980" } ] - }, + }, "caption": { - "created_time": "1289323345", - "text": "Flowers.", + "created_time": "1289323345", + "text": "Flowers.", "from": { - "username": "mxwife19", - "first_name": " Motocross Wife ", - "last_name": "", - "type": "user", + "username": "mxwife19", + "first_name": " Motocross Wife ", + "last_name": "", + "type": "user", "id": "486164" - }, + }, "id": "2606139" - }, - "link": "http://localhost:8000/p/M4k8/", + }, + "link": "http://localhost:8000/p/M4k8/", "likes": { "count": 10 - }, - "created_time": "1289323344", + }, + "created_time": "1289323344", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/f1c61e7a27a14e72ba96e9958f2e4737_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/f1c61e7a27a14e72ba96e9958f2e4737_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/f1c61e7a27a14e72ba96e9958f2e4737_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/f1c61e7a27a14e72ba96e9958f2e4737_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/f1c61e7a27a14e72ba96e9958f2e4737_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/f1c61e7a27a14e72ba96e9958f2e4737_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3377468", + }, + "user_has_liked": false, + "id": "3377468", "user": { - "username": "mxwife19", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_486164_75sq_1289094513.jpg", + "username": "mxwife19", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_486164_75sq_1289094513.jpg", "id": "486164" } - }, + }, { - "type": "image", - "location": null, + "type": "image", + "tags": [], + "location": null, "comments": { - "count": 16, + "count": 16, "data": [ { - "created_time": "1289320621", - "text": "My newest cousin.", + "created_time": "1289320621", + "text": "My newest cousin.", "from": { - "username": "hollymamas", - "first_name": "Holly", - "last_name": "Sanford", - "type": "user", + "username": "hollymamas", + "first_name": "Holly", + "last_name": "Sanford", + "type": "user", "id": "4034" - }, + }, "id": "2601790" - }, + }, { - "created_time": "1289320827", - "text": "Congrats", + "created_time": "1289320827", + "text": "Congrats", "from": { - "username": "leprechaun1", - "first_name": "", - "last_name": "", - "type": "user", + "username": "leprechaun1", + "first_name": "", + "last_name": "", + "type": "user", "id": "373482" - }, + }, "id": "2602180" - }, + }, { - "created_time": "1289320943", - "text": "Great pic!!!", + "created_time": "1289320943", + "text": "Great pic!!!", "from": { - "username": "mayu0130", - "first_name": "mayu.", - "last_name": "k", - "type": "user", + "username": "mayu0130", + "first_name": "mayu.", + "last_name": "k", + "type": "user", "id": "131007" - }, + }, "id": "2602409" - }, + }, { - "created_time": "1289321170", - "text": "Ссcongrad!", + "created_time": "1289321170", + "text": "Ссcongrad!", "from": { - "username": "kitashov", - "first_name": "Roman", - "last_name": "Kitashov", - "type": "user", + "username": "kitashov", + "first_name": "Roman", + "last_name": "Kitashov", + "type": "user", "id": "167653" - }, + }, "id": "2602771" - }, + }, { - "created_time": "1289321423", - "text": "Congratulation :)", + "created_time": "1289321423", + "text": "Congratulation :)", "from": { - "username": "adamiko", - "first_name": "Adam", - "last_name": "Shul", - "type": "user", + "username": "adamiko", + "first_name": "Adam", + "last_name": "Shul", + "type": "user", "id": "455755" - }, + }, "id": "2603159" - }, + }, { - "created_time": "1289321729", - "text": "Sweet!", + "created_time": "1289321729", + "text": "Sweet!", "from": { - "username": "iphonequeen", - "first_name": "Jane", - "last_name": "Jones", - "type": "user", + "username": "iphonequeen", + "first_name": "Jane", + "last_name": "Jones", + "type": "user", "id": "6392" - }, + }, "id": "2603652" - }, + }, { - "created_time": "1289322362", - "text": "Awwww Congrats to everyone! : )", + "created_time": "1289322362", + "text": "Awwww Congrats to everyone! : )", "from": { - "username": "gigi22", - "first_name": "GIna", - "last_name": "G", - "type": "user", + "username": "gigi22", + "first_name": "GIna", + "last_name": "G", + "type": "user", "id": "31123" - }, + }, "id": "2604672" - }, + }, { - "created_time": "1289322396", - "text": "It's moving!!", + "created_time": "1289322396", + "text": "It's moving!!", "from": { - "username": "harunattu", - "first_name": "Haruna", - "last_name": "OH!", - "type": "user", + "username": "harunattu", + "first_name": "Haruna", + "last_name": "OH!", + "type": "user", "id": "150735" - }, + }, "id": "2604725" - }, + }, { - "created_time": "1289323373", - "text": "Congratulation", + "created_time": "1289323373", + "text": "Congratulation", "from": { - "username": "fralu", - "first_name": "Frank", - "last_name": "Lucas", - "type": "user", + "username": "fralu", + "first_name": "Frank", + "last_name": "Lucas", + "type": "user", "id": "417307" - }, + }, "id": "2606180" - }, + }, { - "created_time": "1289323996", - "text": "Congratulation :-", + "created_time": "1289323996", + "text": "Congratulation :-", "from": { - "username": "ilpolve", - "first_name": "", - "last_name": "", - "type": "user", + "username": "ilpolve", + "first_name": "", + "last_name": "", + "type": "user", "id": "575447" - }, + }, "id": "2607077" - }, + }, { - "created_time": "1289324736", - "text": "Congrats! I Love the pic, its power & tenderness.", + "created_time": "1289324736", + "text": "Congrats! I Love the pic, its power & tenderness.", "from": { - "username": "eros_sana", - "first_name": "Eros", - "last_name": "Sana", - "type": "user", + "username": "eros_sana", + "first_name": "Eros", + "last_name": "Sana", + "type": "user", "id": "199902" - }, + }, "id": "2608132" - }, + }, { - "created_time": "1289324754", - "text": "おめでとう", + "created_time": "1289324754", + "text": "おめでとう", "from": { - "username": "phpkiyopon", - "first_name": "kiyo", - "last_name": "yaka", - "type": "user", + "username": "phpkiyopon", + "first_name": "kiyo", + "last_name": "yaka", + "type": "user", "id": "155272" - }, + }, "id": "2608159" - }, + }, { - "created_time": "1289324935", - "text": "Wonderful! So happy for you!!", + "created_time": "1289324935", + "text": "Wonderful! So happy for you!!", "from": { - "username": "brittneyinazkaban", - "first_name": "Brittney", - "last_name": "Shepherd", - "type": "user", + "username": "brittneyinazkaban", + "first_name": "Brittney", + "last_name": "Shepherd", + "type": "user", "id": "85933" - }, + }, "id": "2608459" - }, + }, { - "created_time": "1289325221", - "text": "Congratulation :-", + "created_time": "1289325221", + "text": "Congratulation :-", "from": { - "username": "ryltar", - "first_name": "Lorenzo", - "last_name": "", - "type": "user", + "username": "ryltar", + "first_name": "Lorenzo", + "last_name": "", + "type": "user", "id": "421198" - }, + }, "id": "2608905" - }, + }, { - "created_time": "1289325772", - "text": "Amazing moment", + "created_time": "1289325772", + "text": "Amazing moment", "from": { - "username": "missmagee", - "first_name": "", - "last_name": "", - "type": "user", + "username": "missmagee", + "first_name": "", + "last_name": "", + "type": "user", "id": "402953" - }, + }, "id": "2609679" - }, + }, { - "created_time": "1289325862", - "text": "Congrats!!!!", + "created_time": "1289325862", + "text": "Congrats!!!!", "from": { - "username": "megaera", - "first_name": "Teresa", - "last_name": "C", - "type": "user", + "username": "megaera", + "first_name": "Teresa", + "last_name": "C", + "type": "user", "id": "207272" - }, + }, "id": "2609789" } ] - }, + }, "caption": { - "created_time": "1289320621", - "text": "My newest cousin.", + "created_time": "1289320621", + "text": "My newest cousin.", "from": { - "username": "hollymamas", - "first_name": "Holly", - "last_name": "Sanford", - "type": "user", + "username": "hollymamas", + "first_name": "Holly", + "last_name": "Sanford", + "type": "user", "id": "4034" - }, + }, "id": "2601790" - }, - "link": "http://localhost:8000/p/M3eJ/", + }, + "link": "http://localhost:8000/p/M3eJ/", "likes": { "count": 94 - }, - "created_time": "1289320607", + }, + "created_time": "1289320607", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/e6af847cd9ac4b37b0206f93d6988248_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/e6af847cd9ac4b37b0206f93d6988248_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/e6af847cd9ac4b37b0206f93d6988248_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/e6af847cd9ac4b37b0206f93d6988248_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/e6af847cd9ac4b37b0206f93d6988248_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/09/e6af847cd9ac4b37b0206f93d6988248_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3372937", + }, + "user_has_liked": false, + "id": "3372937", "user": { - "username": "hollymamas", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_4034_75sq_1289291394.jpg", + "username": "hollymamas", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_4034_75sq_1289291394.jpg", "id": "4034" } - }, + }, { - "type": "image", - "location": null, + "type": "image", + "tags": [], + "location": null, "comments": { - "count": 3, + "count": 3, "data": [ { - "created_time": "1289086857", - "text": ":-", + "created_time": "1289086857", + "text": ":-", "from": { - "username": "dady", - "first_name": "Davide", - "last_name": "Baby", - "type": "user", + "username": "dady", + "first_name": "Davide", + "last_name": "Baby", + "type": "user", "id": "247401" - }, + }, "id": "2176090" - }, + }, { - "created_time": "1289213285", - "text": "That's wonderful!", + "created_time": "1289213285", + "text": "That's wonderful!", "from": { - "username": "gangxtag", - "first_name": "", - "last_name": "", - "type": "user", + "username": "gangxtag", + "first_name": "", + "last_name": "", + "type": "user", "id": "469325" - }, + }, "id": "2411911" - }, + }, { - "created_time": "1289305281", - "text": "Beautiful shot...", + "created_time": "1289305281", + "text": "Beautiful shot...", "from": { - "username": "kreshnik", - "first_name": "Kreshnik", - "last_name": "Rushiti", - "type": "user", + "username": "kreshnik", + "first_name": "Kreshnik", + "last_name": "Rushiti", + "type": "user", "id": "127131" - }, + }, "id": "2570851" } ] - }, - "caption": null, - "link": "http://localhost:8000/p/Ie0c/", + }, + "caption": null, + "link": "http://localhost:8000/p/Ie0c/", "likes": { "count": 51 - }, - "created_time": "1288728562", + }, + "created_time": "1288728562", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/02/3b73c20b92794acfa8a733a5a93de769_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/02/3b73c20b92794acfa8a733a5a93de769_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/02/3b73c20b92794acfa8a733a5a93de769_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/02/3b73c20b92794acfa8a733a5a93de769_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/02/3b73c20b92794acfa8a733a5a93de769_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/02/3b73c20b92794acfa8a733a5a93de769_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "2223388", + }, + "user_has_liked": false, + "id": "2223388", "user": { - "username": "hpedersen", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_366769_75sq_1288532493.jpg", + "username": "hpedersen", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_366769_75sq_1288532493.jpg", "id": "366769" } } diff --git a/fixtures/media_search.json b/fixtures/media_search.json index 9653dcef..b03cf157 100644 --- a/fixtures/media_search.json +++ b/fixtures/media_search.json @@ -1,589 +1,597 @@ { "meta": { "code": 200 - }, + }, "data": [ { - "type": "image", + "type": "image", + "tags": [], "location": { - "latitude": 37.775382999999991, - "id": "74480", - "longitude": -122.223941, + "latitude": 37.775382999999991, + "id": "74480", + "longitude": -122.223941, "name": "Fruitvale BART" - }, + }, "comments": { - "count": 1, + "count": 1, "data": [ { - "created_time": "1288931764", - "text": "Emergency", + "created_time": "1288931764", + "text": "Emergency", "from": { - "username": "catchfoot", - "first_name": "Rachel", - "last_name": "Lightfoot", - "type": "user", + "username": "catchfoot", + "first_name": "Rachel", + "last_name": "Lightfoot", + "type": "user", "id": "208329" - }, + }, "id": "1916879" } ] - }, + }, "caption": { - "created_time": "1288931764", - "text": "Emergency", + "created_time": "1288931764", + "text": "Emergency", "from": { - "username": "catchfoot", - "first_name": "Rachel", - "last_name": "Lightfoot", - "type": "user", + "username": "catchfoot", + "first_name": "Rachel", + "last_name": "Lightfoot", + "type": "user", "id": "208329" - }, + }, "id": "1916879" - }, - "link": "http://localhost:8000/p/J2aL/", + }, + "link": "http://localhost:8000/p/J2aL/", "likes": { "count": 0 - }, - "created_time": "1288931757", + }, + "created_time": "1288931757", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/04/486c002f2fd14e2f8a1aef99dee7b95d_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/04/486c002f2fd14e2f8a1aef99dee7b95d_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/04/486c002f2fd14e2f8a1aef99dee7b95d_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/04/486c002f2fd14e2f8a1aef99dee7b95d_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/04/486c002f2fd14e2f8a1aef99dee7b95d_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/04/486c002f2fd14e2f8a1aef99dee7b95d_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "2582155", + }, + "user_has_liked": false, + "id": "2582155", "user": { - "username": "catchfoot", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_208329_75sq_1288164413.jpg", + "username": "catchfoot", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_208329_75sq_1288164413.jpg", "id": "208329" } - }, + }, { - "type": "image", + "type": "image", + "tags": [], "location": { - "latitude": 37.775289999999998, - "id": "163042", - "longitude": -122.224172, + "latitude": 37.775289999999998, + "id": "163042", + "longitude": -122.224172, "name": "Powderface" - }, + }, "comments": { - "count": 1, + "count": 1, "data": [ { - "created_time": "1288644742", - "text": "Caramel Beignets! Yummy :)", + "created_time": "1288644742", + "text": "Caramel Beignets! Yummy :)", "from": { - "username": "mkram0s", - "first_name": "Kate", - "last_name": "Ramos", - "type": "user", + "username": "mkram0s", + "first_name": "Kate", + "last_name": "Ramos", + "type": "user", "id": "305504" - }, + }, "id": "1494733" } ] - }, + }, "caption": { - "created_time": "1288644742", - "text": "Caramel Beignets! Yummy :)", + "created_time": "1288644742", + "text": "Caramel Beignets! Yummy :)", "from": { - "username": "mkram0s", - "first_name": "Kate", - "last_name": "Ramos", - "type": "user", + "username": "mkram0s", + "first_name": "Kate", + "last_name": "Ramos", + "type": "user", "id": "305504" - }, + }, "id": "1494733" - }, - "link": "http://localhost:8000/p/H9R_/", + }, + "link": "http://localhost:8000/p/H9R_/", "likes": { "count": 0 - }, - "created_time": "1288644633", + }, + "created_time": "1288644633", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/01/7a74951a954a44619b5b2afb64a7b17d_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/01/7a74951a954a44619b5b2afb64a7b17d_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/01/7a74951a954a44619b5b2afb64a7b17d_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/01/7a74951a954a44619b5b2afb64a7b17d_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/01/7a74951a954a44619b5b2afb64a7b17d_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/11/01/7a74951a954a44619b5b2afb64a7b17d_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "2086015", + }, + "user_has_liked": false, + "id": "2086015", "user": { - "username": "mkram0s", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_305504_75sq_1288204024.jpg", + "username": "mkram0s", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_305504_75sq_1288204024.jpg", "id": "305504" } - }, + }, { - "type": "image", + "type": "image", + "tags": [], "location": { - "latitude": 37.775382999999991, - "id": "74480", - "longitude": -122.223941, + "latitude": 37.775382999999991, + "id": "74480", + "longitude": -122.223941, "name": "Fruitvale BART" - }, + }, "comments": { - "count": 1, + "count": 1, "data": [ { - "created_time": "1288232768", - "text": "Bored on Bart ", + "created_time": "1288232768", + "text": "Bored on Bart ", "from": { - "username": "hannahc", - "first_name": "Hannah", - "last_name": "Coughlin ", - "type": "user", + "username": "hannahc", + "first_name": "Hannah", + "last_name": "Coughlin ", + "type": "user", "id": "304020" - }, + }, "id": "1030446" } ] - }, + }, "caption": { - "created_time": "1288232768", - "text": "Bored on Bart ", + "created_time": "1288232768", + "text": "Bored on Bart ", "from": { - "username": "hannahc", - "first_name": "Hannah", - "last_name": "Coughlin ", - "type": "user", + "username": "hannahc", + "first_name": "Hannah", + "last_name": "Coughlin ", + "type": "user", "id": "304020" - }, + }, "id": "1030446" - }, - "link": "http://localhost:8000/p/Fm-m/", + }, + "link": "http://localhost:8000/p/Fm-m/", "likes": { "count": 0 - }, - "created_time": "1288232753", + }, + "created_time": "1288232753", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/27/1406596f54be46b4a538dd1d4b4c6f1e_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/27/1406596f54be46b4a538dd1d4b4c6f1e_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/27/1406596f54be46b4a538dd1d4b4c6f1e_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/27/1406596f54be46b4a538dd1d4b4c6f1e_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/27/1406596f54be46b4a538dd1d4b4c6f1e_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/27/1406596f54be46b4a538dd1d4b4c6f1e_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "1470374", + }, + "user_has_liked": false, + "id": "1470374", "user": { - "username": "hannahc", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_304020_75sq_1288194960.jpg", + "username": "hannahc", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_304020_75sq_1288194960.jpg", "id": "304020" } - }, + }, { - "type": "image", + "type": "image", + "tags": [], "location": { - "latitude": 37.775382999999991, - "id": "74480", - "longitude": -122.223941, + "latitude": 37.775382999999991, + "id": "74480", + "longitude": -122.223941, "name": "Fruitvale BART" - }, + }, "comments": { - "count": 1, + "count": 1, "data": [ { - "created_time": "1288136425", - "text": "Emergency", + "created_time": "1288136425", + "text": "Emergency", "from": { - "username": "rolliefingaz", - "first_name": "Gregory", - "last_name": "Hurcomb", - "type": "user", + "username": "rolliefingaz", + "first_name": "Gregory", + "last_name": "Hurcomb", + "type": "user", "id": "233618" - }, + }, "id": "951869" } ] - }, + }, "caption": { - "created_time": "1288136425", - "text": "Emergency", + "created_time": "1288136425", + "text": "Emergency", "from": { - "username": "rolliefingaz", - "first_name": "Gregory", - "last_name": "Hurcomb", - "type": "user", + "username": "rolliefingaz", + "first_name": "Gregory", + "last_name": "Hurcomb", + "type": "user", "id": "233618" - }, + }, "id": "951869" - }, - "link": "http://localhost:8000/p/FOZf/", + }, + "link": "http://localhost:8000/p/FOZf/", "likes": { "count": 0 - }, - "created_time": "1288136246", + }, + "created_time": "1288136246", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/26/3459f1db2d6d422b950838715a04cb38_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/26/3459f1db2d6d422b950838715a04cb38_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/26/3459f1db2d6d422b950838715a04cb38_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/26/3459f1db2d6d422b950838715a04cb38_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/26/3459f1db2d6d422b950838715a04cb38_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/26/3459f1db2d6d422b950838715a04cb38_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "1369695", + }, + "user_has_liked": false, + "id": "1369695", "user": { - "username": "rolliefingaz", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_233618_75sq_1287785819.jpg", + "username": "rolliefingaz", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_233618_75sq_1287785819.jpg", "id": "233618" } - }, + }, { - "type": "image", + "type": "image", + "tags": [], "location": { - "latitude": 37.775194499999998, - "id": "101132", - "longitude": -122.227087, + "latitude": 37.775194499999998, + "id": "101132", + "longitude": -122.227087, "name": "Guadalajara Mexican Cuisine and Bar" - }, + }, "comments": { - "count": 2, + "count": 2, "data": [ { - "created_time": "1287969851", - "text": "Well hello there beautiful", + "created_time": "1287969851", + "text": "Well hello there beautiful", "from": { - "username": "jorstaff", - "first_name": "Jordan", - "last_name": "Walker", - "type": "user", + "username": "jorstaff", + "first_name": "Jordan", + "last_name": "Walker", + "type": "user", "id": "154425" - }, + }, "id": "818311" - }, + }, { - "created_time": "1288072257", - "text": "Text me next time. I live down the st and I'm ways down for tacos!!", + "created_time": "1288072257", + "text": "Text me next time. I live down the st and I'm ways down for tacos!!", "from": { - "username": "melisaarreguin", - "first_name": "Melisa", - "last_name": "Arreguin", - "type": "user", + "username": "melisaarreguin", + "first_name": "Melisa", + "last_name": "Arreguin", + "type": "user", "id": "122725" - }, + }, "id": "898954" } ] - }, + }, "caption": { - "created_time": "1287969851", - "text": "Well hello there beautiful", + "created_time": "1287969851", + "text": "Well hello there beautiful", "from": { - "username": "jorstaff", - "first_name": "Jordan", - "last_name": "Walker", - "type": "user", + "username": "jorstaff", + "first_name": "Jordan", + "last_name": "Walker", + "type": "user", "id": "154425" - }, + }, "id": "818311" - }, - "link": "http://localhost:8000/p/Ejh5/", + }, + "link": "http://localhost:8000/p/Ejh5/", "likes": { "count": 1 - }, - "created_time": "1287969827", + }, + "created_time": "1287969827", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/24/8a466d809fe442a1a2df024543ae25e8_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/24/8a466d809fe442a1a2df024543ae25e8_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/24/8a466d809fe442a1a2df024543ae25e8_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/24/8a466d809fe442a1a2df024543ae25e8_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/24/8a466d809fe442a1a2df024543ae25e8_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/24/8a466d809fe442a1a2df024543ae25e8_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "1194105", + }, + "user_has_liked": false, + "id": "1194105", "user": { - "username": "jorstaff", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_154425_75sq_1287413622.jpg", + "username": "jorstaff", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_154425_75sq_1287413622.jpg", "id": "154425" } - }, + }, { - "type": "image", + "type": "image", + "tags": [], "location": { - "latitude": 37.775382999999991, - "id": "74480", - "longitude": -122.223941, + "latitude": 37.775382999999991, + "id": "74480", + "longitude": -122.223941, "name": "Fruitvale BART" - }, + }, "comments": { - "count": 1, + "count": 1, "data": [ { - "created_time": "1287674437", - "text": "Morning commute", + "created_time": "1287674437", + "text": "Morning commute", "from": { - "username": "catchfoot", - "first_name": "Rachel", - "last_name": "Lightfoot", - "type": "user", + "username": "catchfoot", + "first_name": "Rachel", + "last_name": "Lightfoot", + "type": "user", "id": "208329" - }, + }, "id": "555932" } ] - }, + }, "caption": { - "created_time": "1287674437", - "text": "Morning commute", + "created_time": "1287674437", + "text": "Morning commute", "from": { - "username": "catchfoot", - "first_name": "Rachel", - "last_name": "Lightfoot", - "type": "user", + "username": "catchfoot", + "first_name": "Rachel", + "last_name": "Lightfoot", + "type": "user", "id": "208329" - }, + }, "id": "555932" - }, - "link": "http://localhost:8000/p/DMl1/", + }, + "link": "http://localhost:8000/p/DMl1/", "likes": { "count": 0 - }, - "created_time": "1287674360", + }, + "created_time": "1287674360", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/21/d9949ca81cb540d798acee59ea94223a_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/21/d9949ca81cb540d798acee59ea94223a_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/21/d9949ca81cb540d798acee59ea94223a_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/21/d9949ca81cb540d798acee59ea94223a_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/21/d9949ca81cb540d798acee59ea94223a_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/21/d9949ca81cb540d798acee59ea94223a_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "838005", + }, + "user_has_liked": false, + "id": "838005", "user": { - "username": "catchfoot", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_208329_75sq_1288164413.jpg", + "username": "catchfoot", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_208329_75sq_1288164413.jpg", "id": "208329" } - }, + }, { - "type": "image", + "type": "image", + "tags": [], "location": { - "latitude": 37.775180799999987, - "id": "68841", - "longitude": -122.2270716, + "latitude": 37.775180799999987, + "id": "68841", + "longitude": -122.2270716, "name": "El Novillo Taco Truck" - }, + }, "comments": { - "count": 4, + "count": 4, "data": [ { - "created_time": "1287585720", - "text": "Carne Asasa Torta. No Onions. ", + "created_time": "1287585720", + "text": "Carne Asasa Torta. No Onions. ", "from": { - "username": "darodriguez", - "first_name": "David", - "last_name": "Rodriguez", - "type": "user", + "username": "darodriguez", + "first_name": "David", + "last_name": "Rodriguez", + "type": "user", "id": "113603" - }, + }, "id": "495555" - }, + }, { - "created_time": "1287585854", - "text": "Hey, I wanted onions. :(", + "created_time": "1287585854", + "text": "Hey, I wanted onions. :(", "from": { - "username": "melisaarreguin", - "first_name": "Melisa", - "last_name": "Arreguin", - "type": "user", + "username": "melisaarreguin", + "first_name": "Melisa", + "last_name": "Arreguin", + "type": "user", "id": "122725" - }, + }, "id": "495675" - }, + }, { - "created_time": "1287603871", - "text": "You got onions. You had the al pastor, remember!?", + "created_time": "1287603871", + "text": "You got onions. You had the al pastor, remember!?", "from": { - "username": "darodriguez", - "first_name": "David", - "last_name": "Rodriguez", - "type": "user", + "username": "darodriguez", + "first_name": "David", + "last_name": "Rodriguez", + "type": "user", "id": "113603" - }, + }, "id": "507172" - }, + }, { - "created_time": "1287606841", - "text": "Oh shiiiit this would have taken everything to the next level", + "created_time": "1287606841", + "text": "Oh shiiiit this would have taken everything to the next level", "from": { - "username": "jorstaff", - "first_name": "Jordan", - "last_name": "Walker", - "type": "user", + "username": "jorstaff", + "first_name": "Jordan", + "last_name": "Walker", + "type": "user", "id": "154425" - }, + }, "id": "508433" } ] - }, + }, "caption": { - "created_time": "1287585720", - "text": "Carne Asasa Torta. No Onions. ", + "created_time": "1287585720", + "text": "Carne Asasa Torta. No Onions. ", "from": { - "username": "darodriguez", - "first_name": "David", - "last_name": "Rodriguez", - "type": "user", + "username": "darodriguez", + "first_name": "David", + "last_name": "Rodriguez", + "type": "user", "id": "113603" - }, + }, "id": "495555" - }, - "link": "http://localhost:8000/p/C5cU/", + }, + "link": "http://localhost:8000/p/C5cU/", "likes": { "count": 1 - }, - "created_time": "1287585671", + }, + "created_time": "1287585671", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/20/ad6e8429c40c40ffabe0813ba302a479_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/20/ad6e8429c40c40ffabe0813ba302a479_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/20/ad6e8429c40c40ffabe0813ba302a479_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/20/ad6e8429c40c40ffabe0813ba302a479_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/20/ad6e8429c40c40ffabe0813ba302a479_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/20/ad6e8429c40c40ffabe0813ba302a479_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "759572", + }, + "user_has_liked": false, + "id": "759572", "user": { - "username": "darodriguez", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_113603_75sq_1287035206.jpg", + "username": "darodriguez", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_113603_75sq_1287035206.jpg", "id": "113603" } - }, + }, { - "type": "image", + "type": "image", + "tags": [], "location": { - "latitude": 37.775180799999987, - "id": "68841", - "longitude": -122.2270716, + "latitude": 37.775180799999987, + "id": "68841", + "longitude": -122.2270716, "name": "El Novillo Taco Truck" - }, + }, "comments": { - "count": 1, + "count": 1, "data": [ { - "created_time": "1287585453", - "text": "Tortas. ", + "created_time": "1287585453", + "text": "Tortas. ", "from": { - "username": "darodriguez", - "first_name": "David", - "last_name": "Rodriguez", - "type": "user", + "username": "darodriguez", + "first_name": "David", + "last_name": "Rodriguez", + "type": "user", "id": "113603" - }, + }, "id": "495311" } ] - }, + }, "caption": { - "created_time": "1287585453", - "text": "Tortas. ", + "created_time": "1287585453", + "text": "Tortas. ", "from": { - "username": "darodriguez", - "first_name": "David", - "last_name": "Rodriguez", - "type": "user", + "username": "darodriguez", + "first_name": "David", + "last_name": "Rodriguez", + "type": "user", "id": "113603" - }, + }, "id": "495311" - }, - "link": "http://localhost:8000/p/C5Wr/", + }, + "link": "http://localhost:8000/p/C5Wr/", "likes": { "count": 0 - }, - "created_time": "1287585407", + }, + "created_time": "1287585407", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/20/1fde15405b6349ef949c9a8f5498868e_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/20/1fde15405b6349ef949c9a8f5498868e_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/20/1fde15405b6349ef949c9a8f5498868e_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/20/1fde15405b6349ef949c9a8f5498868e_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/20/1fde15405b6349ef949c9a8f5498868e_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/20/1fde15405b6349ef949c9a8f5498868e_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "759211", + }, + "user_has_liked": false, + "id": "759211", "user": { - "username": "darodriguez", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_113603_75sq_1287035206.jpg", + "username": "darodriguez", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_113603_75sq_1287035206.jpg", "id": "113603" } } diff --git a/fixtures/tag_recent_media.json b/fixtures/tag_recent_media.json index 740e584a..56c5a3a5 100644 --- a/fixtures/tag_recent_media.json +++ b/fixtures/tag_recent_media.json @@ -1,189 +1,192 @@ { - "pagination": {}, + "pagination": {}, "meta": { "code": 200 - }, + }, "data": [ { - "type": "image", - "location": null, + "type": "image", + "tags": [], + "location": null, "comments": { - "count": 1, + "count": 1, "data": [ { - "created_time": "1287439973", - "text": "Midnight dancing @kevinjaako. #iPhone #photography", + "created_time": "1287439973", + "text": "Midnight dancing @kevinjaako. #iPhone #photography", "from": { - "username": "cathycracks", + "username": "cathycracks", "id": "12053" - }, + }, "id": "404609" } ] - }, + }, "caption": { - "created_time": "1287439973", - "text": "Midnight dancing @kevinjaako. #iPhone #photography", + "created_time": "1287439973", + "text": "Midnight dancing @kevinjaako. #iPhone #photography", "from": { - "username": "cathycracks", + "username": "cathycracks", "id": "12053" - }, + }, "id": "404609" - }, - "link": "http://localhost:8000/p/Cbg2/", + }, + "link": "http://localhost:8000/p/Cbg2/", "likes": { "count": 0 - }, - "created_time": "1287439896", + }, + "created_time": "1287439896", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/18/f9d2708b47c54949b9bb48ed676f8d8e_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/18/f9d2708b47c54949b9bb48ed676f8d8e_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/18/f9d2708b47c54949b9bb48ed676f8d8e_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/18/f9d2708b47c54949b9bb48ed676f8d8e_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/18/f9d2708b47c54949b9bb48ed676f8d8e_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/18/f9d2708b47c54949b9bb48ed676f8d8e_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "636982", + }, + "user_has_liked": false, + "id": "636982", "user": { - "username": "cathycracks", - "first_name": "Cathy", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_12053_75sq_1287858628.jpg", - "id": "12053", + "username": "cathycracks", + "first_name": "Cathy", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_12053_75sq_1287858628.jpg", + "id": "12053", "last_name": "Wang" } - }, + }, { - "type": "image", - "location": null, + "type": "image", + "tags": [], + "location": null, "comments": { - "count": 1, + "count": 1, "data": [ { - "created_time": "1287237712", - "text": "Van wie is eigenlijk mijn #iPhone", + "created_time": "1287237712", + "text": "Van wie is eigenlijk mijn #iPhone", "from": { - "username": "edwardvanmeel", + "username": "edwardvanmeel", "id": "46088" - }, + }, "id": "283284" } ] - }, + }, "caption": { - "created_time": "1287237712", - "text": "Van wie is eigenlijk mijn #iPhone", + "created_time": "1287237712", + "text": "Van wie is eigenlijk mijn #iPhone", "from": { - "username": "edwardvanmeel", + "username": "edwardvanmeel", "id": "46088" - }, + }, "id": "283284" - }, - "link": "http://localhost:8000/p/BvOo/", + }, + "link": "http://localhost:8000/p/BvOo/", "likes": { "count": 1 - }, - "created_time": "1287237652", + }, + "created_time": "1287237652", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/16/8848d8d1b48e44f5b4fe8c86a2e4f949_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/16/8848d8d1b48e44f5b4fe8c86a2e4f949_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/16/8848d8d1b48e44f5b4fe8c86a2e4f949_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/16/8848d8d1b48e44f5b4fe8c86a2e4f949_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/16/8848d8d1b48e44f5b4fe8c86a2e4f949_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/16/8848d8d1b48e44f5b4fe8c86a2e4f949_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "455592", + }, + "user_has_liked": false, + "id": "455592", "user": { - "username": "edwardvanmeel", - "first_name": "Edward", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_46088_75sq_1286621654.jpg", - "id": "46088", + "username": "edwardvanmeel", + "first_name": "Edward", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_46088_75sq_1286621654.jpg", + "id": "46088", "last_name": "van Meel" } - }, + }, { - "type": "image", - "location": null, + "type": "image", + "tags": [], + "location": null, "comments": { - "count": 2, + "count": 2, "data": [ { - "created_time": "1286395811", - "text": "Front-page worthy: @instagramapp. It's gorgeous. #iPhone", + "created_time": "1286395811", + "text": "Front-page worthy: @instagramapp. It's gorgeous. #iPhone", "from": { - "username": "nik", + "username": "nik", "id": "5273" - }, + }, "id": "11380" - }, + }, { - "created_time": "1286728380", - "text": "I love this app too!", + "created_time": "1286728380", + "text": "I love this app too!", "from": { - "username": "venetia", + "username": "venetia", "id": "42294" - }, + }, "id": "86708" } ] - }, + }, "caption": { - "created_time": "1286395811", - "text": "Front-page worthy: @instagramapp. It's gorgeous. #iPhone", + "created_time": "1286395811", + "text": "Front-page worthy: @instagramapp. It's gorgeous. #iPhone", "from": { - "username": "nik", + "username": "nik", "id": "5273" - }, + }, "id": "11380" - }, - "link": "http://localhost:8000/p/DBH/", + }, + "link": "http://localhost:8000/p/DBH/", "likes": { "count": 7 - }, - "created_time": "1286394994", + }, + "created_time": "1286394994", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/06/ea33af4f6d5d4a5da569052e94d6b820_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/06/ea33af4f6d5d4a5da569052e94d6b820_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/06/ea33af4f6d5d4a5da569052e94d6b820_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/06/ea33af4f6d5d4a5da569052e94d6b820_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/06/ea33af4f6d5d4a5da569052e94d6b820_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2010/10/06/ea33af4f6d5d4a5da569052e94d6b820_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "12359", + }, + "user_has_liked": false, + "id": "12359", "user": { - "username": "nik", - "first_name": "nik", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_5273_75sq_1286392268.jpg", - "id": "5273", + "username": "nik", + "first_name": "nik", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_5273_75sq_1286392268.jpg", + "id": "5273", "last_name": "" } } diff --git a/fixtures/user_liked_media.json b/fixtures/user_liked_media.json index db23d274..f073bfef 100644 --- a/fixtures/user_liked_media.json +++ b/fixtures/user_liked_media.json @@ -1,79 +1,80 @@ { "pagination": { - "next_url": "http://localhost:8000/publicapi/v1/users/self/feed?q=iphone&lat=37.771&access_token=DEBUG&lng=-122.221&max_id=3382520", - "next_max_like_id": 50 - }, + "next_url": "http://localhost:8000/publicapi/v1/users/self/feed?q=iphone&lat=37.771&access_token=DEBUG&lng=-122.221&max_id=3382520", + "next_max_like_id": 50 + }, "meta": { "code": 200 - }, + }, "data": [ { - "type": "image", + "type": "image", + "tags": [], "location": { - "latitude": 37.781089999999999, - "id": null, - "longitude": -122.3946, + "latitude": 37.781089999999999, + "id": null, + "longitude": -122.3946, "name": null - }, + }, "comments": { - "count": 1, + "count": 1, "data": [ { - "created_time": "1296272101", - "text": "O hai.", + "created_time": "1296272101", + "text": "O hai.", "from": { - "username": "mikeyk", - "first_name": "Mike", - "last_name": "Krieger!!", - "type": "user", + "username": "mikeyk", + "first_name": "Mike", + "last_name": "Krieger!!", + "type": "user", "id": "4" - }, + }, "id": "2611719" } ] - }, + }, "caption": { - "created_time": "1296272101", - "text": "O hai.", + "created_time": "1296272101", + "text": "O hai.", "from": { - "username": "mikeyk", - "first_name": "Mike", - "last_name": "Krieger!!", - "type": "user", + "username": "mikeyk", + "first_name": "Mike", + "last_name": "Krieger!!", + "type": "user", "id": "4" - }, + }, "id": "2611719" - }, - "link": "http://localhost:8000/p/M5z4/", + }, + "link": "http://localhost:8000/p/M5z4/", "likes": { "count": 0 - }, - "created_time": "1296673135", + }, + "created_time": "1296673135", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2011/02/02/b6073350d89644ccb253b89ead6c75da_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2011/02/02/b6073350d89644ccb253b89ead6c75da_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2011/02/02/b6073350d89644ccb253b89ead6c75da_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2011/02/02/b6073350d89644ccb253b89ead6c75da_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2011/02/02/b6073350d89644ccb253b89ead6c75da_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2011/02/02/b6073350d89644ccb253b89ead6c75da_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3382520", + }, + "user_has_liked": false, + "id": "3382520", "user": { - "username": "mikeyk", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_4_75sq_1292324747_debug.jpg", + "username": "mikeyk", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_4_75sq_1292324747_debug.jpg", "id": "4" } - } + } ] } diff --git a/fixtures/user_media_feed.json b/fixtures/user_media_feed.json index 731b6f38..00e7139e 100644 --- a/fixtures/user_media_feed.json +++ b/fixtures/user_media_feed.json @@ -1,78 +1,79 @@ { "pagination": { - "next_url": "http://localhost:8000/publicapi/v1/users/self/feed?q=iphone&lat=37.771&access_token=DEBUG&lng=-122.221&max_id=3382520", + "next_url": "http://localhost:8000/publicapi/v1/users/self/feed?q=iphone&lat=37.771&access_token=DEBUG&lng=-122.221&max_id=3382520", "next_max_id": 3382520 - }, + }, "meta": { "code": 200 - }, + }, "data": [ { - "type": "image", + "type": "image", "location": { - "latitude": 37.781089999999999, - "id": null, - "longitude": -122.3946, + "latitude": 37.781089999999999, + "id": null, + "longitude": -122.3946, "name": null - }, + }, + "tags": [], "comments": { - "count": 1, + "count": 1, "data": [ { - "created_time": "1296272101", - "text": "O hai.", + "created_time": "1296272101", + "text": "O hai.", "from": { - "username": "mikeyk", - "first_name": "Mike", - "last_name": "Krieger!!", - "type": "user", + "username": "mikeyk", + "first_name": "Mike", + "last_name": "Krieger!!", + "type": "user", "id": "4" - }, + }, "id": "2611719" } ] - }, + }, "caption": { - "created_time": "1296272101", - "text": "O hai.", + "created_time": "1296272101", + "text": "O hai.", "from": { - "username": "mikeyk", - "first_name": "Mike", - "last_name": "Krieger!!", - "type": "user", + "username": "mikeyk", + "first_name": "Mike", + "last_name": "Krieger!!", + "type": "user", "id": "4" - }, + }, "id": "2611719" - }, - "link": "http://localhost:8000/p/M5z4/", + }, + "link": "http://localhost:8000/p/M5z4/", "likes": { "count": 0 - }, - "created_time": "1296673135", + }, + "created_time": "1296673135", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2011/02/02/b6073350d89644ccb253b89ead6c75da_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2011/02/02/b6073350d89644ccb253b89ead6c75da_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2011/02/02/b6073350d89644ccb253b89ead6c75da_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2011/02/02/b6073350d89644ccb253b89ead6c75da_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2011/02/02/b6073350d89644ccb253b89ead6c75da_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2011/02/02/b6073350d89644ccb253b89ead6c75da_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3382520", + }, + "user_has_liked": false, + "id": "3382520", "user": { - "username": "mikeyk", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_4_75sq_1292324747_debug.jpg", + "username": "mikeyk", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_4_75sq_1292324747_debug.jpg", "id": "4" } - } + } ] } diff --git a/fixtures/user_recent_media.json b/fixtures/user_recent_media.json index e54b5de7..9aaecfe5 100644 --- a/fixtures/user_recent_media.json +++ b/fixtures/user_recent_media.json @@ -1,76 +1,77 @@ { "pagination": { - "next_url": "http://localhost:8000/publicapi/v1/users/4/media/recent?q=iphone&lat=37.771&access_token=DEBUG&lng=-122.221&max_id=3382522&count=1", + "next_url": "http://localhost:8000/publicapi/v1/users/4/media/recent?q=iphone&lat=37.771&access_token=DEBUG&lng=-122.221&max_id=3382522&count=1", "next_max_id": 3382522 - }, + }, "meta": { "code": 200 - }, + }, "data": [ { - "type": "image", + "type": "image", + "tags": [], "location": { - "latitude": 37.781109999999998, - "id": null, - "longitude": -122.3947, + "latitude": 37.781109999999998, + "id": null, + "longitude": -122.3947, "name": null - }, + }, "comments": { - "count": 1, + "count": 1, "data": [ { - "created_time": "1296713291", - "text": "Earear", + "created_time": "1296713291", + "text": "Earear", "from": { - "username": "mikeyk", - "first_name": "Mike", - "last_name": "Krieger!!", - "type": "user", + "username": "mikeyk", + "first_name": "Mike", + "last_name": "Krieger!!", + "type": "user", "id": "4" - }, + }, "id": "2611728" } ] - }, + }, "caption": { - "created_time": "1296713291", - "text": "Earear", + "created_time": "1296713291", + "text": "Earear", "from": { - "username": "mikeyk", - "first_name": "Mike", - "last_name": "Krieger!!", - "type": "user", + "username": "mikeyk", + "first_name": "Mike", + "last_name": "Krieger!!", + "type": "user", "id": "4" - }, + }, "id": "2611728" - }, - "link": "http://localhost:8000/p/M5z6/", + }, + "link": "http://localhost:8000/p/M5z6/", "likes": { "count": 0 - }, - "created_time": "1296713289", + }, + "created_time": "1296713289", "images": { "low_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2011/02/02/1ce5f3f490a640ca9068e6000c91adc5_6.jpg", - "width": 480, + "url": "http://distillery-dev.s3.amazonaws.com/media/2011/02/02/1ce5f3f490a640ca9068e6000c91adc5_6.jpg", + "width": 480, "height": 480 - }, + }, "thumbnail": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2011/02/02/1ce5f3f490a640ca9068e6000c91adc5_5.jpg", - "width": 150, + "url": "http://distillery-dev.s3.amazonaws.com/media/2011/02/02/1ce5f3f490a640ca9068e6000c91adc5_5.jpg", + "width": 150, "height": 150 - }, + }, "standard_resolution": { - "url": "http://distillery-dev.s3.amazonaws.com/media/2011/02/02/1ce5f3f490a640ca9068e6000c91adc5_7.jpg", - "width": 612, + "url": "http://distillery-dev.s3.amazonaws.com/media/2011/02/02/1ce5f3f490a640ca9068e6000c91adc5_7.jpg", + "width": 612, "height": 612 } - }, - "user_has_liked": false, - "id": "3382522", + }, + "user_has_liked": false, + "id": "3382522", "user": { - "username": "mikeyk", - "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_4_75sq_1292324747_debug.jpg", + "username": "mikeyk", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_4_75sq_1292324747_debug.jpg", "id": "4" } } diff --git a/instagram/models.py b/instagram/models.py index e5c29a93..c655775d 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -68,7 +68,6 @@ def object_from_dictionary(cls, entry): if entry['caption']: new_media.caption = Comment.object_from_dictionary(entry['caption']) - new_media.tags = entry['tags'] if entry['tags']: new_media.tags = [] for tag in entry['tags']: diff --git a/tests.py b/tests.py index 8d9cbac2..cc525d49 100755 --- a/tests.py +++ b/tests.py @@ -105,9 +105,13 @@ def test_like_media(self): self.api.like_media(media_id=4) self.api.unlike_media(media_id=4) + """ + TEMP; disabled this test while we add + a proper response to create_media_comment def test_comment_media(self): comment = self.api.create_media_comment(media_id=4, text='test') self.api.delete_comment(media_id=4, comment_id=comment.id) + """ def test_user_feed(self): self.api.user_media_feed(count=50) From 7b7e3f431fe106bd7b6a935870e5e07657adc213 Mon Sep 17 00:00:00 2001 From: Mike Krieger Date: Fri, 2 Mar 2012 11:45:00 -0800 Subject: [PATCH 58/95] version bump --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index add923aa..371ecd42 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup(name="python-instagram", - version="0.7.2", + version="0.7.3", description="Instagram API client", license="MIT", install_requires=["simplejson","httplib2"], From 7ce1f49e1073f748fcc734149344a00986dae3b9 Mon Sep 17 00:00:00 2001 From: Pamela Fox Date: Mon, 9 Apr 2012 14:14:43 -0700 Subject: [PATCH 59/95] Changed message to error_message, as that's what the API returns to me. Example JSON: {"code": 400, "error_type": "OAuthException", "error_message": "Redirect URI doesn't match original redirect URI"} --- instagram/oauth2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instagram/oauth2.py b/instagram/oauth2.py index d54a7393..852e8260 100644 --- a/instagram/oauth2.py +++ b/instagram/oauth2.py @@ -108,7 +108,7 @@ def exchange_for_access_token(self, code=None, username=None, password=None, sco response, content = http_object.request(url, method="POST", body=data) parsed_content = simplejson.loads(content) if int(response['status']) != 200: - raise OAuth2AuthExchangeError(parsed_content.get("message", "")) + raise OAuth2AuthExchangeError(parsed_content.get("error_message", "")) return parsed_content['access_token'], parsed_content['user'] From 53e1a7f85215f963b37587b69e0cb85adb524240 Mon Sep 17 00:00:00 2001 From: Pamela Fox Date: Wed, 11 Apr 2012 15:15:46 -0700 Subject: [PATCH 60/95] Adding more information to the README. The API is written in a way such that many of the methods are generated, and that makes it a bit tricky to see what arguments and return values correspond to each method. I've added examples and method signatures to the README so that other folks don't have to dig around the code as much. --- README.md | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 131 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 5f023475..9467da2b 100644 --- a/README.md +++ b/README.md @@ -18,26 +18,149 @@ Discussion Visit [our Google Group](http://groups.google.com/group/instagram-api-developers) to discuss the Instagram API. -Obtaining an access token +Authentication ----- -You can use the provided get_access_token.py script to obtain an access token for yourself. -It will prompt you for your app's Client ID, Client Secret, and Redirect URI, -and walk you through instructions for getting your own access token for your app. -Usage ------ +Instagram API uses the OAuth2 protocol for authentication, but not all functionality requires authentication. +See the docs for more information: http://instagr.am/developer/auth/ + +### Obtaining an access token + +If you're using a method that requires authentication and need an access token, you can use the provided get_access_token.py script to obtain an access token for yourself. +It will prompt you for your app's Client ID, Client Secret, and Redirect URI, and walk you through instructions for getting your own access token for your app. + +### Authenticating a user + +The provided sample app shows a simple OAuth flow for authenticating a user and getting an access token for them. + +### Using an access token + +Once you have an access token (whether via the script or from the user flow), you can pass that token into the InstagramAPI constructor: + from instagram.client import InstagramAPI - access_token = "..." + access_token = "YOUR_ACCESS_TOKEN" api = InstagramAPI(access_token=access_token) + recent_media, next = instagram_client.user_recent_media(user_id=user.instagram_userid, count=count) + for media in recent_media: + print media.caption.text + +### Making unauthenticated requests + +For methods that don't require authentication, you can just pass your client ID and optionally client secret into the InstagramAPI +constructor: + + api = InstagramAPI(client_id='YOUR_CLIENT_ID', client_secret='YOUR_CLIENT_SECRET') popular_media = api.media_popular(count=20) for media in popular_media: print media.images['standard_resolution'].url + +Real-time Subscriptions: +----- + +See the docs for more on real-time subscriptions: http://instagr.am/developer/realtime/ + +You can use the API to subscribe to users, tags, locations, or geographies: + + # Subscribe to updates for all users authenticated to your app + api.create_subscription(object='user', aspect='media', callback_url='http://mydomain.com/hook/instagram') + # Subscribe to all media tagged with 'fox' + api.create_subscription(object='tag', object_id='fox', aspect='media', callback_url='http://mydomain.com/hook/instagram') + # Subscribe to all media in a given location + api.create_subscription(object='location', object_id='1257285', aspect='media', callback_url='http://mydomain.com/hook/instagram') + # Subscribe to all media in a geographic area + api.create_subscription(object='geography', lat=35.657872, lng=139.70232, radius=1000, aspect='media', callback_url='http://mydomain.com/hook/instagram') + +Along with that, you would typically register subscription "reactors" for processing the different subscription types: + + # React to user type updates + reactor = subscriptions.SubscriptionsReactor() + reactor.register_callback(subscriptions.SubscriptionType.USER, process_user_update) + +See the provided sample app for an example of making a subscription, reacting to it, an processing the updates. + +You can also use the API to list and delete subscriptions: + api.list_subscriptions() + api.delete_subscriptions(id=342342) + + +Data Retrieval: +----- + +See the endpoints docs for more on these methods: http://instagr.am/developer/endpoints/ + +The methods with a * return two values, where the second is a pagination parameter. Here's an example of retrieving recent media: + + recent_media, next = api.user_recent_media() + photos = [] + for media in recent_media: + photos.append('' % media.images['thumbnail'].url) + + +Users: http://instagr.am/developer/endpoints/users/ + + api.user(user_id) + api.user_media_feed()* + api.user_liked_media()* + api.user_recent_media(user_id, count, max_id)* + api.user_search(q, count, lat, lng, min_timestamp, max_timestamp) + + +Relationships: http://instagr.am/developer/endpoints/relationships/ + + api.user_incoming_requests() + api.user_follows(user_id)* + api.user_followed_by(user_id)* + api.follow_user(user_id) + api.unfollow_user(user_id) + api.block_user(user_id) + api.unblock_user(user_id) + api.approve_user_request(user_id) + api.ignore_user_request(user_id) + + +Media: http://instagr.am/developer/endpoints/media/ + + api.media(media_id) + api.media_popular(count, max_id) + api.media_search(q, count, lat, lng, min_timestamp, max_timestamp) + +Comments: http://instagr.am/developer/endpoints/comments/ + + api.media_comments(media_id) + api.create_media_comment(media_id, text) + api.delete_comment(media_id, comment_id) + +Likes: http://instagr.am/developer/endpoints/likes/ + + api.media_likes(media_id) + api.like_media(media_id) + api.unlike_media(media_id) + +Tags: http://instagr.am/developer/endpoints/tags/ + + api.tag(tag_name) + api.tag_recent_media(count, max_id, tag_name)* + api.tag_search(q, count)* + +Locations: http://instagr.am/developer/endpoints/locations/ + + api.location(location_id) + api.location_recent_media(count, max_id, location_id)* + api.location_search(q, count, lat, lng, foursquare_id) + +Geographies: http://instagr.am/developer/endpoints/geographies/ + + api.geography_recent_media(count, max_id, geography_id)* + + Sample app ------ -We also provide a one-file sample app using bottle (you'll have to 'pip install bottle' first). To try it out: +This repository includes a one-file sample app that uses the bottle framework and demonstrates +authentication, subscriptions, and update processing. To try it out: + * Download bottle if you don't already have it: pip install bottle * Set your redirect URI to 'http://localhost:8515/oauth_callback' in your dev profile * Open up sample\_app.py, update it with your client\_id and secret, and set redirect URI to 'http://localhost:8515/oauth_callback' * Run the file; it will host a local server on port 8515. From 0d1726a627e6e2bb768cf6099f9b1ea0eaa72c0b Mon Sep 17 00:00:00 2001 From: Michael Helmick Date: Mon, 28 May 2012 13:09:12 -0400 Subject: [PATCH 61/95] Added user_relationship method, more verbose simplejson import, except ValueError on loads funcs * ```user_relationship``` - determine whether the relationship status between the current user and the target user * more verbose ```simplejson``` import * except ValueError on ```simplejson.loads()``` - This needs to be caught. An example can be ```python ingr = InstagramAPI(**creds) ingr.user_relationship(user_id='mikehelmick') ``` ```mikehelmick``` is not a user **id** so Instagram returns 404 Couldn't find this page. Sorry. Which would cause L134 of ```bind.py``` to fail because ```raise InstagramAPIError(status_code, content_obj['meta']['error_type'], content_obj['meta']['error_message']) ``` ```meta``` does not exist cause the response wasn't parsed. --- instagram/bind.py | 20 ++++++++++++++++++-- instagram/client.py | 9 +++++++++ instagram/models.py | 6 ++++++ instagram/subscriptions.py | 24 +++++++++++++++++++++--- 4 files changed, 54 insertions(+), 5 deletions(-) diff --git a/instagram/bind.py b/instagram/bind.py index 6e7db910..77e69ae7 100644 --- a/instagram/bind.py +++ b/instagram/bind.py @@ -1,7 +1,18 @@ import urllib from oauth2 import OAuth2Request import re -import simplejson + +try: + import simplejson +except ImportError: + try: + import json as simplejson + except ImportError: + try: + from django.utils import simplejson + except ImportError: + raise ImportError('A json library is required to use this python library. Lol, yay for being verbose. ;)') + re_path_template = re.compile('{\w+}') @@ -91,7 +102,12 @@ def _do_api_request(self, url, method="GET", body=None, headers=None): response, content = OAuth2Request(self.api).make_request(url, method=method, body=body, headers=headers) if response['status'] == '503': raise InstagramAPIError(response['status'], "Rate limited", "Your client is making too many request per second") - content_obj = simplejson.loads(content) + + try: + content_obj = simplejson.loads(content) + except ValueError: + raise InstagramClientError('Unable to parse response, not valid JSON.') + api_responses = [] status_code = content_obj['meta']['code'] if status_code == 200: diff --git a/instagram/client.py b/instagram/client.py index 07958349..e5abc6b0 100644 --- a/instagram/client.py +++ b/instagram/client.py @@ -174,6 +174,15 @@ def __init__(self, *args, **kwargs): requires_target_user=True, response_type="entry") + user_relationship = bind_method( + method="GET", + path="/users/{user_id}/relationship", + root_class=Relationship, + accepts_parameters=["user_id"], + paginates=False, + requires_target_user=True, + response_type="entry") + def _make_relationship_shortcut(action): def _inner(self, *args, **kwargs): return self.change_user_relationship(user_id=kwargs.get("user_id"), diff --git a/instagram/models.py b/instagram/models.py index c655775d..e81270aa 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -154,3 +154,9 @@ def __init__(self, incoming_status="none", outgoing_status="none", target_user_i self.incoming_status = incoming_status self.outgoing_status = outgoing_status self.target_user_is_private = target_user_is_private + + def __unicode__(self): + follows = False if self.outgoing_status == 'none' else True + followed = False if self.incoming_status == 'none' else True + + return "Relationship: (Follows: %s, Followed by: %s)" % (follows, followed) diff --git a/instagram/subscriptions.py b/instagram/subscriptions.py index 1a946a05..ad54fd48 100644 --- a/instagram/subscriptions.py +++ b/instagram/subscriptions.py @@ -1,6 +1,16 @@ import hmac import hashlib -import simplejson + +try: + import simplejson +except ImportError: + try: + import json as simplejson + except ImportError: + try: + from django.utils import simplejson + except ImportError: + raise ImportError('A json library is required to use this python library. Lol, yay for being verbose. ;)') class SubscriptionType: @@ -10,7 +20,11 @@ class SubscriptionType: LOCATION = 'location' -class SubscriptionVerifyError(Exception): +class SubscriptionError(Exception): + pass + + +class SubscriptionVerifyError(SubscriptionError): pass @@ -28,7 +42,11 @@ def process(self, client_secret, raw_response, x_hub_signature): if not self._verify_signature(client_secret, raw_response, x_hub_signature): raise SubscriptionVerifyError("X-Hub-Signature and hmac digest did not match") - response = simplejson.loads(raw_response) + try: + response = simplejson.loads(raw_response) + except ValueError: + raise SubscriptionError('Unable to parse response, not valid JSON.') + for update in response: self._process_update(update) From 884aa80279237b15251c1430d45bd91e0580cb26 Mon Sep 17 00:00:00 2001 From: Michael Helmick Date: Mon, 28 May 2012 13:12:04 -0400 Subject: [PATCH 62/95] 0.8.0, Bumping the minor because we *added a method* --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 371ecd42..6eb85c68 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup(name="python-instagram", - version="0.7.3", + version="0.8.0", description="Instagram API client", license="MIT", install_requires=["simplejson","httplib2"], From 0e48d1848dd58eccb80d1059a3b7692ace8c08c8 Mon Sep 17 00:00:00 2001 From: Michael Helmick Date: Tue, 29 May 2012 13:36:31 -0400 Subject: [PATCH 63/95] Add user_relationship to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9467da2b..17531318 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,7 @@ Relationships: http://instagr.am/developer/endpoints/relationships/ api.unblock_user(user_id) api.approve_user_request(user_id) api.ignore_user_request(user_id) + api.user_relationship(user_id) Media: http://instagr.am/developer/endpoints/media/ From 95dc1c526e7bdc27fc9a33a82fe18bd7aa944a6c Mon Sep 17 00:00:00 2001 From: Mike Krieger Date: Tue, 29 May 2012 10:57:46 -0700 Subject: [PATCH 64/95] Central json import logic --- instagram/bind.py | 12 +----------- instagram/json_import.py | 10 ++++++++++ instagram/subscriptions.py | 13 +------------ 3 files changed, 12 insertions(+), 23 deletions(-) create mode 100644 instagram/json_import.py diff --git a/instagram/bind.py b/instagram/bind.py index 77e69ae7..b981440e 100644 --- a/instagram/bind.py +++ b/instagram/bind.py @@ -1,17 +1,7 @@ import urllib from oauth2 import OAuth2Request import re - -try: - import simplejson -except ImportError: - try: - import json as simplejson - except ImportError: - try: - from django.utils import simplejson - except ImportError: - raise ImportError('A json library is required to use this python library. Lol, yay for being verbose. ;)') +from json_import import simplejson re_path_template = re.compile('{\w+}') diff --git a/instagram/json_import.py b/instagram/json_import.py new file mode 100644 index 00000000..87984799 --- /dev/null +++ b/instagram/json_import.py @@ -0,0 +1,10 @@ +try: + import simplejson +except ImportError: + try: + import json as simplejson + except ImportError: + try: + from django.utils import simplejson + except ImportError: + raise ImportError('A json library is required to use this python library') diff --git a/instagram/subscriptions.py b/instagram/subscriptions.py index ad54fd48..298fdd14 100644 --- a/instagram/subscriptions.py +++ b/instagram/subscriptions.py @@ -1,17 +1,6 @@ import hmac import hashlib - -try: - import simplejson -except ImportError: - try: - import json as simplejson - except ImportError: - try: - from django.utils import simplejson - except ImportError: - raise ImportError('A json library is required to use this python library. Lol, yay for being verbose. ;)') - +from json_import import simplejson class SubscriptionType: TAG = 'tag' From 00289065bfcec69259a42011e9fc8ae41386c219 Mon Sep 17 00:00:00 2001 From: tonyxiao Date: Sun, 8 Jul 2012 12:59:11 -0700 Subject: [PATCH 65/95] Fixed bug in sample_app.py. The access_token needs to be unpacked from returned arguments. --- sample_app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sample_app.py b/sample_app.py index 81fe74ef..e37eab35 100644 --- a/sample_app.py +++ b/sample_app.py @@ -32,7 +32,7 @@ def on_callback(): if not code: return 'Missing code' try: - access_token = unauthenticated_api.exchange_code_for_access_token(code) + access_token, user_info = unauthenticated_api.exchange_code_for_access_token(code) if not access_token: return 'Could not get access token' From b8e88f11c2a3202629a30011b00869544f8d3125 Mon Sep 17 00:00:00 2001 From: Benn Eichhorn Date: Tue, 31 Jul 2012 13:07:02 +1000 Subject: [PATCH 66/95] Handle OAuthRateLimitException from Nginx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit content_obj =  {     "code": 420,     "error_type": "OAuthRateLimitException",     "error_message": "You have exceeded the maximum number of requests per hour. You have performed a total of 5001 requests in the last hour. Our general maximum request limit is set at 5000 requests per hour." } --- instagram/bind.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/instagram/bind.py b/instagram/bind.py index b981440e..4b2aa80f 100644 --- a/instagram/bind.py +++ b/instagram/bind.py @@ -98,6 +98,11 @@ def _do_api_request(self, url, method="GET", body=None, headers=None): except ValueError: raise InstagramClientError('Unable to parse response, not valid JSON.') + # Handle OAuthRateLimitExceeded from Instagram's Nginx which uses different format to documented api responses + if content_obj.get('code') == '420': + error_message = content_obj.get('error_message') or "Your client is making too many request per second" + raise InstagramAPIError('420', "Rate limited", error_message) + api_responses = [] status_code = content_obj['meta']['code'] if status_code == 200: From 07c4d5619275b9463fde75832b19dd87d7c09358 Mon Sep 17 00:00:00 2001 From: Benn Eichhorn Date: Tue, 31 Jul 2012 13:19:48 +1000 Subject: [PATCH 67/95] Handling any non meta response --- instagram/bind.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/instagram/bind.py b/instagram/bind.py index 4b2aa80f..5acfde48 100644 --- a/instagram/bind.py +++ b/instagram/bind.py @@ -99,9 +99,11 @@ def _do_api_request(self, url, method="GET", body=None, headers=None): raise InstagramClientError('Unable to parse response, not valid JSON.') # Handle OAuthRateLimitExceeded from Instagram's Nginx which uses different format to documented api responses - if content_obj.get('code') == '420': - error_message = content_obj.get('error_message') or "Your client is making too many request per second" - raise InstagramAPIError('420', "Rate limited", error_message) + if not content_obj.has_key('meta'): + if content_obj.get('code') == '420': + error_message = content_obj.get('error_message') or "Your client is making too many request per second" + raise InstagramAPIError(420, "Rate limited", error_message) + raise InstagramAPIError(content_obj.has_key('code'), content_obj.has_key('error_type'), content_obj.has_key('error_message')) api_responses = [] status_code = content_obj['meta']['code'] From f81532a496c35e9a268ca1c986eb254969bce010 Mon Sep 17 00:00:00 2001 From: Benn Eichhorn Date: Tue, 31 Jul 2012 13:22:29 +1000 Subject: [PATCH 68/95] Fix comparison type bug --- instagram/bind.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instagram/bind.py b/instagram/bind.py index 5acfde48..da28ae38 100644 --- a/instagram/bind.py +++ b/instagram/bind.py @@ -100,7 +100,7 @@ def _do_api_request(self, url, method="GET", body=None, headers=None): # Handle OAuthRateLimitExceeded from Instagram's Nginx which uses different format to documented api responses if not content_obj.has_key('meta'): - if content_obj.get('code') == '420': + if content_obj.get('code') == 420: error_message = content_obj.get('error_message') or "Your client is making too many request per second" raise InstagramAPIError(420, "Rate limited", error_message) raise InstagramAPIError(content_obj.has_key('code'), content_obj.has_key('error_type'), content_obj.has_key('error_message')) From aefb118df2e1bfa54708b5a7bd63e9721b429e65 Mon Sep 17 00:00:00 2001 From: henninge Date: Tue, 7 Aug 2012 11:48:27 +0200 Subject: [PATCH 69/95] Add pagination_format parameter to api methods. --- instagram/bind.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/instagram/bind.py b/instagram/bind.py index b981440e..28688df6 100644 --- a/instagram/bind.py +++ b/instagram/bind.py @@ -47,6 +47,10 @@ class InstagramAPIMethod(object): def __init__(self, api, *args, **kwargs): self.api = api self.as_generator = kwargs.pop("as_generator", False) + if self.as_generator: + self.pagination_format = 'next_url' + else: + self.pagination_format = kwargs.pop('pagination_format', 'next_url') self.return_json = kwargs.pop("return_json", False) self.max_pages = kwargs.pop("max_pages", 3) self.parameters = {} @@ -87,6 +91,15 @@ def _build_path(self): self.path = self.path.replace(variable, value) self.path = self.path + '.%s' % self.api.format + def _build_pagination_info(self, content_obj): + """Extract pagination information in the desired format.""" + pagination = content_obj.get('pagination', {}) + if self.pagination_format == 'next_url': + return pagination.get('next_url') + if self.pagination_format == 'dict': + return pagination + raise Exception('Invalid value for pagination_format: %s' % self.pagination_format) + def _do_api_request(self, url, method="GET", body=None, headers=None): headers = headers or {} response, content = OAuth2Request(self.api).make_request(url, method=method, body=body, headers=headers) @@ -119,7 +132,7 @@ def _do_api_request(self, url, method="GET", body=None, headers=None): api_responses = self.root_class.object_from_dictionary(data) elif self.response_type == 'empty': pass - return api_responses, content_obj.get('pagination', {}).get('next_url') + return api_responses, self._build_pagination_info(content_obj) else: raise InstagramAPIError(status_code, content_obj['meta']['error_type'], content_obj['meta']['error_message']) From 71e7c05806d90d50588d7f7ffeae2e5ef43791ab Mon Sep 17 00:00:00 2001 From: David Thomas Date: Tue, 13 Nov 2012 21:13:17 -0500 Subject: [PATCH 70/95] Added line break Missing an extra line break that prevented Markdown from displaying code properly. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 17531318..e9016bfd 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ Along with that, you would typically register subscription "reactors" for proces See the provided sample app for an example of making a subscription, reacting to it, an processing the updates. You can also use the API to list and delete subscriptions: + api.list_subscriptions() api.delete_subscriptions(id=342342) From f2f63134bf1a4e2b973a55cf3bdba11c6bf7530f Mon Sep 17 00:00:00 2001 From: David Von Lehman Date: Thu, 16 May 2013 11:30:39 -0700 Subject: [PATCH 71/95] Update location_search method to accept new foursquare_v2_id parameter --- README.md | 2 +- instagram/client.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 17531318..100cb5ca 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,7 @@ Locations: http://instagr.am/developer/endpoints/locations/ api.location(location_id) api.location_recent_media(count, max_id, location_id)* - api.location_search(q, count, lat, lng, foursquare_id) + api.location_search(q, count, lat, lng, foursquare_id, foursquare_v2_id) Geographies: http://instagr.am/developer/endpoints/geographies/ diff --git a/instagram/client.py b/instagram/client.py index e5abc6b0..01cd6d45 100644 --- a/instagram/client.py +++ b/instagram/client.py @@ -128,7 +128,7 @@ def __init__(self, *args, **kwargs): location_search = bind_method( path="/locations/search", - accepts_parameters=SEARCH_ACCEPT_PARAMETERS + ['lat', 'lng', 'foursquare_id'], + accepts_parameters=SEARCH_ACCEPT_PARAMETERS + ['lat', 'lng', 'foursquare_id', 'foursquare_v2_id'], root_class=Location) location = bind_method( From e95c7bd5286df61fba0fc06ee1007f987cc77d2a Mon Sep 17 00:00:00 2001 From: Kenneth Kam Date: Sun, 2 Jun 2013 20:10:29 +0200 Subject: [PATCH 72/95] Fixed example code in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 17531318..442b0603 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Once you have an access token (whether via the script or from the user flow), yo access_token = "YOUR_ACCESS_TOKEN" api = InstagramAPI(access_token=access_token) - recent_media, next = instagram_client.user_recent_media(user_id=user.instagram_userid, count=count) + recent_media, next = api.user_recent_media(user_id="userid", count=10) for media in recent_media: print media.caption.text From d65f26cbef0d389128a0d03b801db1bd0d3289a0 Mon Sep 17 00:00:00 2001 From: isnowfy Date: Mon, 22 Jul 2013 19:52:22 +0800 Subject: [PATCH 73/95] use json_import instead of simplejson --- instagram/oauth2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instagram/oauth2.py b/instagram/oauth2.py index 852e8260..0f8a52d4 100644 --- a/instagram/oauth2.py +++ b/instagram/oauth2.py @@ -1,4 +1,4 @@ -import simplejson +from json_import import simplejson import urllib from httplib2 import Http import mimetypes From 29290badfc135ab08f82e8f3da5ed506e8b8534b Mon Sep 17 00:00:00 2001 From: isnowfy Date: Sun, 18 Aug 2013 23:38:55 +0800 Subject: [PATCH 74/95] add with_next_url implement to easy paginator --- instagram/bind.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/instagram/bind.py b/instagram/bind.py index b981440e..4cf0251a 100644 --- a/instagram/bind.py +++ b/instagram/bind.py @@ -49,6 +49,7 @@ def __init__(self, api, *args, **kwargs): self.as_generator = kwargs.pop("as_generator", False) self.return_json = kwargs.pop("return_json", False) self.max_pages = kwargs.pop("max_pages", 3) + self.with_next_url = kwargs.pop("with_next_url", None) self.parameters = {} self._build_parameters(args, kwargs) self._build_path() @@ -132,11 +133,18 @@ def _paginator_with_url(self, url, method="GET", body=None, headers=None): yield api_responses, url return + def _get_with_next_url(self, url, method="GET", body=None, headers=None): + headers = headers or {} + content, next = self._do_api_request(url, method, body, headers) + return content, next + def execute(self): url, method, body, headers = OAuth2Request(self.api).prepare_request(self.method, self.path, self.parameters, include_secret=self.include_secret) + if self.with_next_url: + return self._get_with_next_url(self.with_next_url, method, body, headers) if self.as_generator: return self._paginator_with_url(url, method, body, headers) else: From 2f4247b762c7acc46e3851610279f8a0d72324b0 Mon Sep 17 00:00:00 2001 From: Gaurav Kalra Date: Sun, 15 Sep 2013 00:51:03 +0530 Subject: [PATCH 75/95] Import simplejson with json_import While using python-instagram with GAE, oauth2.py fails to load simplejson. simplejson should always be loaded with json_import --- instagram/oauth2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instagram/oauth2.py b/instagram/oauth2.py index 852e8260..0f8a52d4 100644 --- a/instagram/oauth2.py +++ b/instagram/oauth2.py @@ -1,4 +1,4 @@ -import simplejson +from json_import import simplejson import urllib from httplib2 import Http import mimetypes From 74ef87f5e793c9b5e8d58a27072d889d82a90b06 Mon Sep 17 00:00:00 2001 From: Dan Gayle Date: Mon, 16 Sep 2013 18:44:37 -0700 Subject: [PATCH 76/95] Update README.md Fixed link to instagram authentication page --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 17531318..fc93da90 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Authentication ----- Instagram API uses the OAuth2 protocol for authentication, but not all functionality requires authentication. -See the docs for more information: http://instagr.am/developer/auth/ +See the docs for more information: http://instagram.com/developer/authentication/ ### Obtaining an access token From a1563a18abbbf2d32761b1955aa30e6c04f087d0 Mon Sep 17 00:00:00 2001 From: Phil Sturgeon Date: Tue, 21 Jan 2014 11:07:37 -0500 Subject: [PATCH 77/95] Add syntax highlighting for Python examples --- README.md | 156 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 91 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index f1a660b8..174dc9db 100644 --- a/README.md +++ b/README.md @@ -37,24 +37,27 @@ The provided sample app shows a simple OAuth flow for authenticating a user and Once you have an access token (whether via the script or from the user flow), you can pass that token into the InstagramAPI constructor: - from instagram.client import InstagramAPI - - access_token = "YOUR_ACCESS_TOKEN" - api = InstagramAPI(access_token=access_token) - recent_media, next = api.user_recent_media(user_id="userid", count=10) - for media in recent_media: - print media.caption.text +``` python +from instagram.client import InstagramAPI + +access_token = "YOUR_ACCESS_TOKEN" +api = InstagramAPI(access_token=access_token) +recent_media, next = api.user_recent_media(user_id="userid", count=10) +for media in recent_media: + print media.caption.text +``` ### Making unauthenticated requests For methods that don't require authentication, you can just pass your client ID and optionally client secret into the InstagramAPI constructor: - api = InstagramAPI(client_id='YOUR_CLIENT_ID', client_secret='YOUR_CLIENT_SECRET') - popular_media = api.media_popular(count=20) - for media in popular_media: - print media.images['standard_resolution'].url - +``` python +api = InstagramAPI(client_id='YOUR_CLIENT_ID', client_secret='YOUR_CLIENT_SECRET') +popular_media = api.media_popular(count=20) +for media in popular_media: + print media.images['standard_resolution'].url +``` Real-time Subscriptions: ----- @@ -63,27 +66,36 @@ See the docs for more on real-time subscriptions: http://instagr.am/developer/re You can use the API to subscribe to users, tags, locations, or geographies: - # Subscribe to updates for all users authenticated to your app - api.create_subscription(object='user', aspect='media', callback_url='http://mydomain.com/hook/instagram') - # Subscribe to all media tagged with 'fox' - api.create_subscription(object='tag', object_id='fox', aspect='media', callback_url='http://mydomain.com/hook/instagram') - # Subscribe to all media in a given location - api.create_subscription(object='location', object_id='1257285', aspect='media', callback_url='http://mydomain.com/hook/instagram') - # Subscribe to all media in a geographic area - api.create_subscription(object='geography', lat=35.657872, lng=139.70232, radius=1000, aspect='media', callback_url='http://mydomain.com/hook/instagram') - +``` python +# Subscribe to updates for all users authenticated to your app +api.create_subscription(object='user', aspect='media', callback_url='http://mydomain.com/hook/instagram') + +# Subscribe to all media tagged with 'fox' +api.create_subscription(object='tag', object_id='fox', aspect='media', callback_url='http://mydomain.com/hook/instagram') + +# Subscribe to all media in a given location +api.create_subscription(object='location', object_id='1257285', aspect='media', callback_url='http://mydomain.com/hook/instagram') + +# Subscribe to all media in a geographic area +api.create_subscription(object='geography', lat=35.657872, lng=139.70232, radius=1000, aspect='media', callback_url='http://mydomain.com/hook/instagram') +``` + Along with that, you would typically register subscription "reactors" for processing the different subscription types: - # React to user type updates - reactor = subscriptions.SubscriptionsReactor() - reactor.register_callback(subscriptions.SubscriptionType.USER, process_user_update) +``` python +# React to user type updates +reactor = subscriptions.SubscriptionsReactor() +reactor.register_callback(subscriptions.SubscriptionType.USER, process_user_update) +``` See the provided sample app for an example of making a subscription, reacting to it, an processing the updates. You can also use the API to list and delete subscriptions: - api.list_subscriptions() - api.delete_subscriptions(id=342342) +``` python +api.list_subscriptions() +api.delete_subscriptions(id=342342) +``` Data Retrieval: @@ -93,69 +105,83 @@ See the endpoints docs for more on these methods: http://instagr.am/developer/en The methods with a * return two values, where the second is a pagination parameter. Here's an example of retrieving recent media: - recent_media, next = api.user_recent_media() - photos = [] - for media in recent_media: - photos.append('' % media.images['thumbnail'].url) - +``` python +recent_media, next = api.user_recent_media() +photos = [] +for media in recent_media: + photos.append('' % media.images['thumbnail'].url) +``` Users: http://instagr.am/developer/endpoints/users/ - api.user(user_id) - api.user_media_feed()* - api.user_liked_media()* - api.user_recent_media(user_id, count, max_id)* - api.user_search(q, count, lat, lng, min_timestamp, max_timestamp) - +``` python +api.user(user_id) +api.user_media_feed()* +api.user_liked_media()* +api.user_recent_media(user_id, count, max_id)* +api.user_search(q, count, lat, lng, min_timestamp, max_timestamp) +``` Relationships: http://instagr.am/developer/endpoints/relationships/ - api.user_incoming_requests() - api.user_follows(user_id)* - api.user_followed_by(user_id)* - api.follow_user(user_id) - api.unfollow_user(user_id) - api.block_user(user_id) - api.unblock_user(user_id) - api.approve_user_request(user_id) - api.ignore_user_request(user_id) - api.user_relationship(user_id) - +``` python +api.user_incoming_requests() +api.user_follows(user_id)* +api.user_followed_by(user_id)* +api.follow_user(user_id) +api.unfollow_user(user_id) +api.block_user(user_id) +api.unblock_user(user_id) +api.approve_user_request(user_id) +api.ignore_user_request(user_id) +api.user_relationship(user_id) +``` Media: http://instagr.am/developer/endpoints/media/ - api.media(media_id) - api.media_popular(count, max_id) - api.media_search(q, count, lat, lng, min_timestamp, max_timestamp) +``` python +api.media(media_id) +api.media_popular(count, max_id) +api.media_search(q, count, lat, lng, min_timestamp, max_timestamp) +``` Comments: http://instagr.am/developer/endpoints/comments/ - api.media_comments(media_id) - api.create_media_comment(media_id, text) - api.delete_comment(media_id, comment_id) +``` python +api.media_comments(media_id) +api.create_media_comment(media_id, text) +api.delete_comment(media_id, comment_id) +``` Likes: http://instagr.am/developer/endpoints/likes/ - api.media_likes(media_id) - api.like_media(media_id) - api.unlike_media(media_id) +``` python +api.media_likes(media_id) +api.like_media(media_id) +api.unlike_media(media_id) +``` Tags: http://instagr.am/developer/endpoints/tags/ - api.tag(tag_name) - api.tag_recent_media(count, max_id, tag_name)* - api.tag_search(q, count)* +``` python +api.tag(tag_name) +api.tag_recent_media(count, max_id, tag_name)* +api.tag_search(q, count)* +``` Locations: http://instagr.am/developer/endpoints/locations/ - api.location(location_id) - api.location_recent_media(count, max_id, location_id)* - api.location_search(q, count, lat, lng, foursquare_id) +``` python +api.location(location_id) +api.location_recent_media(count, max_id, location_id)* +api.location_search(q, count, lat, lng, foursquare_id) +``` Geographies: http://instagr.am/developer/endpoints/geographies/ - api.geography_recent_media(count, max_id, geography_id)* - +``` python +api.geography_recent_media(count, max_id, geography_id)* +``` Sample app ------ From 6bc555b93e09ab18a5778487cf3eb47329e83098 Mon Sep 17 00:00:00 2001 From: henninge Date: Mon, 3 Feb 2014 04:05:07 +0100 Subject: [PATCH 78/95] Set version to our own. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 6eb85c68..03a985f7 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup(name="python-instagram", - version="0.8.0", + version="0.8.0powll1", description="Instagram API client", license="MIT", install_requires=["simplejson","httplib2"], From 445f6edcbdd844856b04e5257f16ae7e8150a8df Mon Sep 17 00:00:00 2001 From: Joe Hura Date: Wed, 2 Apr 2014 09:08:14 +1100 Subject: [PATCH 79/95] Media now supports video, has a 'type' field and additional url accessors Unit tests updated --- fixtures/user_recent_media.json | 84 ++++++++++++++++++++++++++++++++- instagram/models.py | 29 +++++++++++- tests.py | 51 +++++++++++++++++--- 3 files changed, 156 insertions(+), 8 deletions(-) diff --git a/fixtures/user_recent_media.json b/fixtures/user_recent_media.json index 9aaecfe5..cb6fece7 100644 --- a/fixtures/user_recent_media.json +++ b/fixtures/user_recent_media.json @@ -74,6 +74,88 @@ "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_4_75sq_1292324747_debug.jpg", "id": "4" } + }, + { + "type": "video", + "videos": { + "low_resolution": { + "url": "http://distilleryvesper9-13.ak.instagram.com/090d06dad9cd11e2aa0912313817975d_102.mp4", + "width": 480, + "height": 480 + }, + "standard_resolution": { + "url": "http://distilleryvesper9-13.ak.instagram.com/090d06dad9cd11e2aa0912313817975d_101.mp4", + "width": 640, + "height": 640 + }}, + "users_in_photo": null, + "filter": "Vesper", + "tags": [], + "comments": { + "data": [{ + "created_time": "1279332030", + "text": "Love the sign here", + "from": { + "username": "mikeyk", + "full_name": "Mikey Krieger", + "id": "4", + "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1242695_75sq_1293915800.jpg" + }, + "id": "8" + }, + { + "created_time": "1279341004", + "text": "Chilako taco", + "from": { + "username": "kevin", + "full_name": "Kevin S", + "id": "3", + "profile_picture": "..." + }, + "id": "3" + }], + "count": 2 + }, + "caption": null, + "likes": { + "count": 1, + "data": [{ + "username": "mikeyk", + "full_name": "Mikeyk", + "id": "4", + "profile_picture": "..." + }] + }, + "link": "http://instagr.am/p/D/", + "user": { + "username": "kevin", + "full_name": "Kevin S", + "profile_picture": "...", + "bio": "...", + "website": "...", + "id": "3" + }, + "created_time": "1279340983", + "images": { + "low_resolution": { + "url": "http://distilleryimage2.ak.instagram.com/11f75f1cd9cc11e2a0fd22000aa8039a_6.jpg", + "width": 306, + "height": 306 + }, + "thumbnail": { + "url": "http://distilleryimage2.ak.instagram.com/11f75f1cd9cc11e2a0fd22000aa8039a_5.jpg", + "width": 150, + "height": 150 + }, + "standard_resolution": { + "url": "http://distilleryimage2.ak.instagram.com/11f75f1cd9cc11e2a0fd22000aa8039a_7.jpg", + "width": 612, + "height": 612 + } + }, + "id": "3", + "location": null } + ] -} \ No newline at end of file +} diff --git a/instagram/models.py b/instagram/models.py index e81270aa..b4ff9787 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -24,6 +24,12 @@ def __unicode__(self): return "Image: %s" % self.url +class Video(Image): + + def __unicode__(self): + return "Video: %s" % self.url + + class Media(ApiModel): def __init__(self, id=None, **kwargs): @@ -32,7 +38,21 @@ def __init__(self, id=None, **kwargs): setattr(self, key, value) def get_standard_resolution_url(self): - return self.images['standard_resolution'].url + if self.type == 'image': + return self.images['standard_resolution'].url + else: + return self.videos['standard_resolution'].url + + def get_low_resolution_url(self): + if self.type == 'image': + return self.images['low_resolution'].url + else: + return self.videos['low_resolution'].url + + + def get_thumbnail_url(self): + return self.images['thumbnail'].url + def __unicode__(self): return "Media: %s" % self.id @@ -40,12 +60,19 @@ def __unicode__(self): @classmethod def object_from_dictionary(cls, entry): new_media = Media(id=entry['id']) + new_media.type = entry['type'] new_media.user = User.object_from_dictionary(entry['user']) + new_media.images = {} for version, version_info in entry['images'].iteritems(): new_media.images[version] = Image.object_from_dictionary(version_info) + if new_media.type == 'video': + new_media.videos = {} + for version, version_info in entry['videos'].iteritems(): + new_media.videos[version] = Video.object_from_dictionary(version_info) + if 'user_has_liked' in entry: new_media.user_has_liked = entry['user_has_liked'] new_media.like_count = entry['likes']['count'] diff --git a/tests.py b/tests.py index cc525d49..5ca03670 100755 --- a/tests.py +++ b/tests.py @@ -1,9 +1,10 @@ -#! /usr/bin/python +#!/usr/bin/env python import types -import sys -import simplejson -import time +try: + import simplejson as json +except ImportError: + import json import getpass import unittest import urlparse @@ -42,7 +43,7 @@ def request(self, url, method="GET", body=None, headers={}): fl = open('fixtures/%s.json' % fn_name) content = fl.read() - json_content = simplejson.loads(content) + json_content = json.loads(content) status = json_content['meta']['code'] return { 'status': status @@ -125,7 +126,45 @@ def test_user_liked_media(self): self.api.user_liked_media(count=10) def test_user_recent_media(self): - self.api.user_recent_media(count=10) + media, url = self.api.user_recent_media(count=10) + + self.assertTrue( all( [hasattr(obj, 'type') for obj in media] ) ) + + image = media[0] + self.assertEquals( + image.get_standard_resolution_url(), + "http://distillery-dev.s3.amazonaws.com/media/2011/02/02/1ce5f3f490a640ca9068e6000c91adc5_7.jpg") + + self.assertEquals( + image.get_low_resolution_url(), + "http://distillery-dev.s3.amazonaws.com/media/2011/02/02/1ce5f3f490a640ca9068e6000c91adc5_6.jpg") + + self.assertEquals( + image.get_thumbnail_url(), + "http://distillery-dev.s3.amazonaws.com/media/2011/02/02/1ce5f3f490a640ca9068e6000c91adc5_5.jpg") + + self.assertEquals( False, hasattr(image, 'videos') ) + + video = media[1] + self.assertEquals( + video.get_standard_resolution_url(), + video.videos['standard_resolution'].url) + + self.assertEquals( + video.get_standard_resolution_url(), + "http://distilleryvesper9-13.ak.instagram.com/090d06dad9cd11e2aa0912313817975d_101.mp4") + + self.assertEquals( + video.get_low_resolution_url(), + "http://distilleryvesper9-13.ak.instagram.com/090d06dad9cd11e2aa0912313817975d_102.mp4") + + self.assertEquals( + video.get_thumbnail_url(), + "http://distilleryimage2.ak.instagram.com/11f75f1cd9cc11e2a0fd22000aa8039a_5.jpg") + + + + def test_user_search(self): self.api.user_search('mikeyk', 10) From 3fe546d75714e17ce068b67b75c9842628b55b78 Mon Sep 17 00:00:00 2001 From: Jon Heaton Date: Mon, 7 Apr 2014 14:08:55 +0100 Subject: [PATCH 80/95] README update with CLA and new License and version 1 bump --- LICENSE.md | 30 +++++++++++++++++++++++ README.md | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++--- setup.py | 2 +- 3 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..3796a715 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,30 @@ +BSD License + +For python-instagram software + +Copyright (c) 2014, Facebook, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name Facebook nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/README.md b/README.md index f1a660b8..ee1e9112 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,19 @@ Requires * simplejson -Discussion ------- +Instagram REST and Search APIs +------------------------------ +Our [developer site](http://instagram.com/developer) documents all the Instagram REST and Search APIs. + + +Blog +---------------------------- +The [Developer Blog] features news and important announcements about the Instagram Platform. You will also find tutorials and best practices to help you build great platform integrations. Make sure to subscribe to the RSS feed not to miss out on new posts: [http://developers.instagram.com](http://developers.instagram.com). -Visit [our Google Group](http://groups.google.com/group/instagram-api-developers) to discuss the Instagram API. + +Community +---------------------- +The [Stack Overflow community](http://stackoverflow.com/questions/tagged/instagram/) is a great place to ask API related questions or if you need help with your code. Make sure to tag your questions with the Instagram tag to get fast answers from other fellow developers and members of the Instagram team. Authentication @@ -167,3 +176,60 @@ authentication, subscriptions, and update processing. To try it out: * Open up sample\_app.py, update it with your client\_id and secret, and set redirect URI to 'http://localhost:8515/oauth_callback' * Run the file; it will host a local server on port 8515. * Try visiting http://localhost:8515 in your browser + +Contributing +------------ +In the spirit of [free software](http://www.fsf.org/licensing/essays/free-sw.html), **everyone** is encouraged to help improve this project. + +Here are some ways *you* can contribute: + +* by using alpha, beta, and prerelease versions +* by reporting bugs +* by suggesting new features +* by writing or editing documentation +* by writing specifications +* by writing code (**no patch is too small**: fix typos, add comments, clean up inconsistent whitespace) +* by refactoring code +* by closing [issues](http://github.com/Instagram/python-instagram/issues) +* by reviewing patches + + +Submitting an Issue +------------------- +We use the [GitHub issue tracker](https://github.com/Instagram/python-instagram/issues) to track bugs and +features. Before submitting a bug report or feature request, check to make sure it hasn't already +been submitted. You can indicate support for an existing issue by voting it up. When submitting a +bug report, please include a [Gist](http://gist.github.com/) that includes a stack trace and any +details that may be necessary to reproduce the bug, including your version number, and +operating system. Ideally, a bug report should include a pull request with failing specs. + +Instagram has a [bounty program](https://www.facebook.com/whitehat/) for the safe +disclosure of security bugs. In those cases, please go through the process +outlined on that page and do not file a public issue. + + +Submitting a Pull Request +------------------------- +1. Fork the project. +2. Create a topic branch. +3. Implement your feature or bug fix. +4. Run python tests.py . +5. Add a test for your feature or bug fix. +6. Run python tests.py . If your changes are not 100% covered, go back to step 5. +7. Commit and push your changes. +8. Submit a pull request. +9. If you haven't already, complete the Contributor License Agreement ("CLA"). + +Contributor License Agreement ("CLA") +_____________________________________ +In order to accept your pull request, we need you to submit a CLA. You only need +to do this once to work on any of Instagram's or Facebook's open source projects. + +Complete your CLA here: [https://code.facebook.com/cla](https://code.facebook.com/cla) + + +Copyright +--------- +Copyright (c) 2014, Facebook, Inc. All rights reserved. +By contributing to python-instagram, you agree that your contributions will be licensed under its BSD license. +See [LICENSE](https://github.com/Instagram/python-instagram/blob/master/LICENSE.md) for details. diff --git a/setup.py b/setup.py index 6eb85c68..6450fcd1 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup(name="python-instagram", - version="0.8.0", + version="1.0.0", description="Instagram API client", license="MIT", install_requires=["simplejson","httplib2"], From 28d89f6de987738d85cea6826baecfdd41f20671 Mon Sep 17 00:00:00 2001 From: Jon Heaton Date: Thu, 24 Apr 2014 10:52:00 +0100 Subject: [PATCH 81/95] added x-ratelimt support to client obj --- instagram/bind.py | 2 + instagram/client.py | 4 +- sample_app.py | 177 ++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 175 insertions(+), 8 deletions(-) diff --git a/instagram/bind.py b/instagram/bind.py index 4cf0251a..1243e9e2 100644 --- a/instagram/bind.py +++ b/instagram/bind.py @@ -101,6 +101,8 @@ def _do_api_request(self, url, method="GET", body=None, headers=None): api_responses = [] status_code = content_obj['meta']['code'] + self.api.x_ratelimit_remaining = response.get("x-ratelimit-remaining",None) + self.api.x_ratelimit = response.get("x-ratelimit-limit",None) if status_code == 200: if not self.objectify_response: return content_obj, None diff --git a/instagram/client.py b/instagram/client.py index e5abc6b0..b2de92da 100644 --- a/instagram/client.py +++ b/instagram/client.py @@ -17,6 +17,8 @@ class InstagramAPI(oauth2.OAuth2API): access_token_url = "https://api.instagram.com/oauth/access_token" protocol = "https" api_name = "Instagram" + x_ratelimit_remaining = None + x_ratelimit = None def __init__(self, *args, **kwargs): format = kwargs.get('format', 'json') @@ -33,7 +35,7 @@ def __init__(self, *args, **kwargs): media_search = bind_method( path="/media/search", - accepts_parameters=SEARCH_ACCEPT_PARAMETERS + ['lat', 'lng', 'min_timestamp', 'max_timestamp'], + accepts_parameters=SEARCH_ACCEPT_PARAMETERS + ['lat', 'lng', 'min_timestamp', 'max_timestamp', 'distance'], root_class=Media) media_likes = bind_method( diff --git a/sample_app.py b/sample_app.py index e37eab35..dbbd150c 100644 --- a/sample_app.py +++ b/sample_app.py @@ -1,12 +1,17 @@ +import bottle_session import bottle -from bottle import route, post, run, request +from bottle import route, redirect, post, run, request from instagram import client, subscriptions bottle.debug(True) +app = bottle.app() +plugin = bottle_session.SessionPlugin(cookie_lifetime=600) +app.install(plugin) + CONFIG = { - 'client_id': '', - 'client_secret': '', + 'client_id': 'dc8c85c161354f7ca30eb604659ad050', + 'client_secret': 'a028e0af338e40c8bdc9b64d8bb93841', 'redirect_uri': 'http://localhost:8515/oauth_callback' } @@ -26,24 +31,182 @@ def home(): except Exception, e: print e +def get_nav(): + nav_menu = ("

Python Instagram

" + "
    " + "
  • User Recent Media Calls user_recent_media - Get a list of a user's most recent media
  • " + "
  • User Media Feed Calls user_media_feed - Get the currently authenticated user's media feed uses pagination
  • " + "
  • Location Recent Media Calls location_recent_media - Get a list of recent media at a given location, in this case, the Instagram office
  • " + "
  • Media Search Calls media_search - Get a list of media close to a given latitude and longitude
  • " + "
  • Popular Media Calls media_popular - Get a list of the overall most popular media items
  • " + "
  • User Search Calls user_search - Search for users on instagram, by name or username
  • " + "
  • Location Search Calls location_search - Search for a location by lat/lng
  • " + "
  • Tags Search for tags, view tag info and get media by tag
  • " + "
") + + return nav_menu + @route('/oauth_callback') -def on_callback(): +def on_callback(session): code = request.GET.get("code") if not code: return 'Missing code' try: access_token, user_info = unauthenticated_api.exchange_code_for_access_token(code) + print "access token= " + access_token if not access_token: return 'Could not get access token' - + api = client.InstagramAPI(access_token=access_token) + session['access_token']=access_token + except Exception, e: + print e + return get_nav() + +@route('/recent') +def on_recent(session): + access_token = session.get('access_token') + content = "

User Recent Media

" + if not access_token: + return 'Missing Access Token' + try: api = client.InstagramAPI(access_token=access_token) recent_media, next = api.user_recent_media() photos = [] for media in recent_media: + if(media.type == 'video'): + photos.append('' % (media.videos['low_resolution'].url)) + else: + photos.append('' % (media.images['thumbnail'].url)) + content += ''.join(photos) + except Exception, e: + print e + return "%s %s
Remaining API Calls = %s/%s" % (get_nav(),content,api.x_ratelimit_remaining,api.x_ratelimit) + +@route('/user_media_feed') +def on_user_media_feed(session): + access_token = session.get('access_token') + content = "

User Media Feed

" + if not access_token: + return 'Missing Access Token' + try: + api = client.InstagramAPI(access_token=access_token) + media_feed, next = api.user_media_feed() + photos = [] + for media in media_feed: photos.append('' % media.images['thumbnail'].url) - return ''.join(photos) + counter = 1 + while next and counter < 3: + media_feed, next = api.user_media_feed(with_next_url=next) + for media in media_feed: + photos.append('' % media.images['thumbnail'].url) + counter += 1 + content += ''.join(photos) except Exception, e: - print e + print e + return "%s %s
Remaining API Calls = %s/%s" % (get_nav(),content,api.x_ratelimit_remaining,api.x_ratelimit) + +@route('/location_recent_media') +def location_recent_media(session): + access_token = session.get('access_token') + content = "

Location Recent Media

" + if not access_token: + return 'Missing Access Token' + try: + api = client.InstagramAPI(access_token=access_token) + recent_media, next = api.location_recent_media(location_id=514276) + photos = [] + for media in recent_media: + photos.append('' % media.images['thumbnail'].url) + content += ''.join(photos) + except Exception, e: + print e + return "%s %s
Remaining API Calls = %s/%s" % (get_nav(),content,api.x_ratelimit_remaining,api.x_ratelimit) + +@route('/media_search') +def media_search(session): + access_token = session.get('access_token') + content = "

Media Search

" + if not access_token: + return 'Missing Access Token' + try: + api = client.InstagramAPI(access_token=access_token) + media_search = api.media_search(lat="37.7808851",lng="-122.3948632",distance=1000) + photos = [] + for media in media_search: + photos.append('' % media.images['thumbnail'].url) + content += ''.join(photos) + except Exception, e: + print e + return "%s %s
Remaining API Calls = %s/%s" % (get_nav(),content,api.x_ratelimit_remaining,api.x_ratelimit) + +@route('/media_popular') +def media_popular(session): + access_token = session.get('access_token') + content = "

Popular Media

" + if not access_token: + return 'Missing Access Token' + try: + api = client.InstagramAPI(access_token=access_token) + media_search = api.media_popular() + photos = [] + for media in media_search: + photos.append('' % media.images['thumbnail'].url) + content += ''.join(photos) + except Exception, e: + print e + return "%s %s
Remaining API Calls = %s/%s" % (get_nav(),content,api.x_ratelimit_remaining,api.x_ratelimit) + +@route('/user_search') +def user_search(session): + access_token = session.get('access_token') + content = "

User Search

" + if not access_token: + return 'Missing Access Token' + try: + api = client.InstagramAPI(access_token=access_token) + user_search = api.user_search(q="Instagram") + users = [] + for user in user_search: + users.append('
  • %s
  • ' % (user.profile_picture,user.username)) + content += ''.join(users) + except Exception, e: + print e + return "%s %s
    Remaining API Calls = %s/%s" % (get_nav(),content,api.x_ratelimit_remaining,api.x_ratelimit) + +@route('/location_search') +def location_search(session): + access_token = session.get('access_token') + content = "

    Location Search

    " + if not access_token: + return 'Missing Access Token' + try: + api = client.InstagramAPI(access_token=access_token) + location_search = api.location_search(lat="37.7808851",lng="-122.3948632",distance=1000) + locations = [] + for location in location_search: + locations.append('
  • %s Map
  • ' % (location.name,location.point.latitude,location.point.longitude)) + content += ''.join(locations) + except Exception, e: + print e + return "%s %s
    Remaining API Calls = %s/%s" % (get_nav(),content,api.x_ratelimit_remaining,api.x_ratelimit) + +@route('/tag_search') +def tag_search(session): + access_token = session.get('access_token') + content = "

    Tag Search

    " + if not access_token: + return 'Missing Access Token' + try: + api = client.InstagramAPI(access_token=access_token) + tag_search, next_tag = api.tag_search(q="catband") + tag_recent_media, next = api.tag_recent_media(tag_name=tag_search[0].name) + photos = [] + for tag_media in tag_recent_media: + photos.append('' % tag_media.images['thumbnail'].url) + content += ''.join(photos) + except Exception, e: + print e + return "%s %s
    Remaining API Calls = %s/%s" % (get_nav(),content,api.x_ratelimit_remaining,api.x_ratelimit) @route('/realtime_callback') @post('/realtime_callback') From 32704a3d12284b2e9b4cb911868269e78b9fb443 Mon Sep 17 00:00:00 2001 From: Jon Heaton Date: Thu, 24 Apr 2014 10:55:44 +0100 Subject: [PATCH 82/95] sample app update --- sample_app.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sample_app.py b/sample_app.py index dbbd150c..a6754e08 100644 --- a/sample_app.py +++ b/sample_app.py @@ -10,8 +10,8 @@ app.install(plugin) CONFIG = { - 'client_id': 'dc8c85c161354f7ca30eb604659ad050', - 'client_secret': 'a028e0af338e40c8bdc9b64d8bb93841', + 'client_id': '', + 'client_secret': '', 'redirect_uri': 'http://localhost:8515/oauth_callback' } @@ -74,7 +74,7 @@ def on_recent(session): photos = [] for media in recent_media: if(media.type == 'video'): - photos.append('' % (media.videos['low_resolution'].url)) + photos.append('' % (media.videos['low_resolution'].url)) else: photos.append('' % (media.images['thumbnail'].url)) content += ''.join(photos) From 07b4032dfe59feda09394b7cf63c2029dd8b8adc Mon Sep 17 00:00:00 2001 From: John Boiles Date: Fri, 2 May 2014 12:34:15 -0700 Subject: [PATCH 83/95] Adding an optional status_code to InstagramClientError --- instagram/bind.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/instagram/bind.py b/instagram/bind.py index 4af36c55..56de7257 100644 --- a/instagram/bind.py +++ b/instagram/bind.py @@ -12,11 +12,15 @@ def encode_string(value): class InstagramClientError(Exception): - def __init__(self, error_message): + def __init__(self, error_message, status_code=None): + self.status_code = status_code self.error_message = error_message def __str__(self): - return self.error_message + if self.status_code: + return "(%s) %s" % (self.status_code, self.error_message) + else: + return self.error_message class InstagramAPIError(Exception): @@ -97,7 +101,7 @@ def _do_api_request(self, url, method="GET", body=None, headers=None): try: content_obj = simplejson.loads(content) except ValueError: - raise InstagramClientError('Unable to parse response, not valid JSON.') + raise InstagramClientError('Unable to parse response, not valid JSON.', status_code=response['status']) # Handle OAuthRateLimitExceeded from Instagram's Nginx which uses different format to documented api responses if not content_obj.has_key('meta'): From c7656b7363b5b1c561cf297578248f180929bfa7 Mon Sep 17 00:00:00 2001 From: Jon Heaton Date: Fri, 9 May 2014 11:37:48 +0100 Subject: [PATCH 84/95] updated sample to use new media methods --- instagram/models.py | 2 ++ sample_app.py | 16 ++++++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/instagram/models.py b/instagram/models.py index b4ff9787..a8394c61 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -6,6 +6,8 @@ class ApiModel(object): @classmethod def object_from_dictionary(cls, entry): # make dict keys all strings + if entry is None: + return "" entry_str_dict = dict([(str(key), value) for key, value in entry.items()]) return cls(**entry_str_dict) diff --git a/sample_app.py b/sample_app.py index a6754e08..44de30fb 100644 --- a/sample_app.py +++ b/sample_app.py @@ -74,9 +74,9 @@ def on_recent(session): photos = [] for media in recent_media: if(media.type == 'video'): - photos.append('' % (media.videos['low_resolution'].url)) + photos.append('' % (media.get_standard_resolution_url())) else: - photos.append('' % (media.images['thumbnail'].url)) + photos.append('' % (media.get_low_resolution_url())) content += ''.join(photos) except Exception, e: print e @@ -93,12 +93,12 @@ def on_user_media_feed(session): media_feed, next = api.user_media_feed() photos = [] for media in media_feed: - photos.append('' % media.images['thumbnail'].url) + photos.append('' % media.get_standard_resolution_url()) counter = 1 while next and counter < 3: media_feed, next = api.user_media_feed(with_next_url=next) for media in media_feed: - photos.append('' % media.images['thumbnail'].url) + photos.append('' % media.get_standard_resolution_url()) counter += 1 content += ''.join(photos) except Exception, e: @@ -116,7 +116,7 @@ def location_recent_media(session): recent_media, next = api.location_recent_media(location_id=514276) photos = [] for media in recent_media: - photos.append('' % media.images['thumbnail'].url) + photos.append('' % media.get_standard_resolution_url()) content += ''.join(photos) except Exception, e: print e @@ -133,7 +133,7 @@ def media_search(session): media_search = api.media_search(lat="37.7808851",lng="-122.3948632",distance=1000) photos = [] for media in media_search: - photos.append('' % media.images['thumbnail'].url) + photos.append('' % media.get_standard_resolution_url()) content += ''.join(photos) except Exception, e: print e @@ -150,7 +150,7 @@ def media_popular(session): media_search = api.media_popular() photos = [] for media in media_search: - photos.append('' % media.images['thumbnail'].url) + photos.append('' % media.get_standard_resolution_url()) content += ''.join(photos) except Exception, e: print e @@ -202,7 +202,7 @@ def tag_search(session): tag_recent_media, next = api.tag_recent_media(tag_name=tag_search[0].name) photos = [] for tag_media in tag_recent_media: - photos.append('' % tag_media.images['thumbnail'].url) + photos.append('' % tag_media.get_standard_resolution_url()) content += ''.join(photos) except Exception, e: print e From 9b0f9f155c988a92c1eb78cb68fd84c03d401c80 Mon Sep 17 00:00:00 2001 From: "Juan E. D." Date: Mon, 12 May 2014 18:56:21 -0300 Subject: [PATCH 85/95] Update README.md Sample app section bottle-session needs to be installed for the sample app to work. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ae35a16a..321aff91 100644 --- a/README.md +++ b/README.md @@ -198,6 +198,7 @@ This repository includes a one-file sample app that uses the bottle framework an authentication, subscriptions, and update processing. To try it out: * Download bottle if you don't already have it: pip install bottle + * Download bottle-session if you don't already have it: pip install bottle-session * Set your redirect URI to 'http://localhost:8515/oauth_callback' in your dev profile * Open up sample\_app.py, update it with your client\_id and secret, and set redirect URI to 'http://localhost:8515/oauth_callback' * Run the file; it will host a local server on port 8515. From 8c2f06b680d4ea0bc749e7f69f3cc14031de85ad Mon Sep 17 00:00:00 2001 From: "Juan E. D." Date: Mon, 12 May 2014 19:11:02 -0300 Subject: [PATCH 86/95] Update README.md Sample app to include Redis An instance of redis is needed for bottles-session to work. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 321aff91..597d6976 100644 --- a/README.md +++ b/README.md @@ -199,6 +199,7 @@ authentication, subscriptions, and update processing. To try it out: * Download bottle if you don't already have it: pip install bottle * Download bottle-session if you don't already have it: pip install bottle-session + * Download and run a redis instance on port 6379 if you don't already have it. Check http://redis.io for instructions. * Set your redirect URI to 'http://localhost:8515/oauth_callback' in your dev profile * Open up sample\_app.py, update it with your client\_id and secret, and set redirect URI to 'http://localhost:8515/oauth_callback' * Run the file; it will host a local server on port 8515. From 3adbb5de2fb413cb2e55b55f69bfdc98433a112f Mon Sep 17 00:00:00 2001 From: Jon Heaton Date: Thu, 22 May 2014 11:39:45 +0100 Subject: [PATCH 87/95] add support for new http status code for ratelimit exceeded --- instagram/bind.py | 6 +++--- setup.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/instagram/bind.py b/instagram/bind.py index c289608a..48306a3e 100644 --- a/instagram/bind.py +++ b/instagram/bind.py @@ -108,7 +108,7 @@ def _build_pagination_info(self, content_obj): def _do_api_request(self, url, method="GET", body=None, headers=None): headers = headers or {} response, content = OAuth2Request(self.api).make_request(url, method=method, body=body, headers=headers) - if response['status'] == '503': + if response['status'] == '503' or response['status'] == '429': raise InstagramAPIError(response['status'], "Rate limited", "Your client is making too many request per second") try: @@ -118,9 +118,9 @@ def _do_api_request(self, url, method="GET", body=None, headers=None): # Handle OAuthRateLimitExceeded from Instagram's Nginx which uses different format to documented api responses if not content_obj.has_key('meta'): - if content_obj.get('code') == 420: + if content_obj.get('code') == 420 or content_obj.get('code') == 429: error_message = content_obj.get('error_message') or "Your client is making too many request per second" - raise InstagramAPIError(420, "Rate limited", error_message) + raise InstagramAPIError(content_obj.get('code'), "Rate limited", error_message) raise InstagramAPIError(content_obj.has_key('code'), content_obj.has_key('error_type'), content_obj.has_key('error_message')) api_responses = [] diff --git a/setup.py b/setup.py index 6450fcd1..ab55317d 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup(name="python-instagram", - version="1.0.0", + version="1.0.1", description="Instagram API client", license="MIT", install_requires=["simplejson","httplib2"], From d58f452c181af6251d7a344239afb8a4887aac71 Mon Sep 17 00:00:00 2001 From: Yohei Sasaki Date: Mon, 2 Jun 2014 15:10:52 +0900 Subject: [PATCH 88/95] Adding the Travis configuration file. --- .travis.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..9c35b372 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +language: python +python: + - "2.6" + - "2.7" +install: + - "pip install ." +script: "python tests.py" + From 16d1bc5b8d3c9c3dcc08cd5e805e649a1f1f0a5e Mon Sep 17 00:00:00 2001 From: Yohei Sasaki Date: Tue, 3 Jun 2014 15:42:45 +0900 Subject: [PATCH 89/95] Support the signed header. Now we can enable the signed header by passing the `client_ips` keyword argument to the Instagram.Client constructor. c = client.InstagramAPI( access_token=access_token, client_ips="1.2.3.4", client_secret="CS" ) c.follow_user(123) # => call API with X-Insta-Forwarded-For signature. If you do not set the client_ips nor client_secret, X-Insta-Forwarded-For is not sent. --- instagram/bind.py | 9 +++++++++ instagram/client.py | 5 +++++ instagram/oauth2.py | 3 ++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/instagram/bind.py b/instagram/bind.py index c289608a..ab1340f0 100644 --- a/instagram/bind.py +++ b/instagram/bind.py @@ -2,6 +2,8 @@ from oauth2 import OAuth2Request import re from json_import import simplejson +import hmac +from hashlib import sha256 re_path_template = re.compile('{\w+}') @@ -41,6 +43,7 @@ class InstagramAPIMethod(object): path = config['path'] method = config.get('method', 'GET') accepts_parameters = config.get("accepts_parameters", []) + signature = config.get("signature", False) requires_target_user = config.get('requires_target_user', False) paginates = config.get('paginates', False) root_class = config.get('root_class', None) @@ -107,6 +110,12 @@ def _build_pagination_info(self, content_obj): def _do_api_request(self, url, method="GET", body=None, headers=None): headers = headers or {} + if self.signature and self.api.client_ips != None and self.api.client_secret != None: + secret = self.api.client_secret + ips = self.api.client_ips + signature = hmac.new(secret, ips, sha256).hexdigest() + headers['X-Insta-Forwarded-For'] = '|'.join([ips, signature]) + response, content = OAuth2Request(self.api).make_request(url, method=method, body=body, headers=headers) if response['status'] == '503': raise InstagramAPIError(response['status'], "Rate limited", "Your client is making too many request per second") diff --git a/instagram/client.py b/instagram/client.py index c59ac866..f9a2266b 100644 --- a/instagram/client.py +++ b/instagram/client.py @@ -46,18 +46,21 @@ def __init__(self, *args, **kwargs): like_media = bind_method( path="/media/{media_id}/likes", method="POST", + signature=True, accepts_parameters=['media_id'], response_type="empty") unlike_media = bind_method( path="/media/{media_id}/likes", method="DELETE", + signature=True, accepts_parameters=['media_id'], response_type="empty") create_media_comment = bind_method( path="/media/{media_id}/comments", method="POST", + signature=True, accepts_parameters=['media_id', 'text'], response_type="empty", root_class=Comment) @@ -65,6 +68,7 @@ def __init__(self, *args, **kwargs): delete_comment = bind_method( path="/media/{media_id}/comments/{comment_id}", method="DELETE", + signature=True, accepts_parameters=['media_id', 'comment_id'], response_type="empty") @@ -170,6 +174,7 @@ def __init__(self, *args, **kwargs): change_user_relationship = bind_method( method="POST", path="/users/{user_id}/relationship", + signature=True, root_class=Relationship, accepts_parameters=["user_id", "action"], paginates=True, diff --git a/instagram/oauth2.py b/instagram/oauth2.py index 0f8a52d4..de2f41ec 100644 --- a/instagram/oauth2.py +++ b/instagram/oauth2.py @@ -24,9 +24,10 @@ class OAuth2API(object): # override with 'Instagram', etc api_name = "Generic API" - def __init__(self, client_id=None, client_secret=None, access_token=None, redirect_uri=None): + def __init__(self, client_id=None, client_secret=None, client_ips=None, access_token=None, redirect_uri=None): self.client_id = client_id self.client_secret = client_secret + self.client_ips = client_ips self.access_token = access_token self.redirect_uri = redirect_uri From 2d480bfcc27fd9483d04d93628e0c11ef9400e0a Mon Sep 17 00:00:00 2001 From: Jon Heaton Date: Tue, 3 Jun 2014 13:43:10 +0100 Subject: [PATCH 90/95] updated sample app to add like and unlike --- sample_app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sample_app.py b/sample_app.py index bb22f40c..1a95a654 100644 --- a/sample_app.py +++ b/sample_app.py @@ -10,8 +10,8 @@ app.install(plugin) CONFIG = { - 'client_id': '0512494a584a40e988476e77c45359ba', - 'client_secret': 'd244e7e964274f82aba3167eb7791410', + 'client_id': '', + 'client_secret': '', 'redirect_uri': 'http://localhost:8515/oauth_callback' } From dd796e73861e7fba44970dd7d273c43c7fab11f9 Mon Sep 17 00:00:00 2001 From: Jon Heaton Date: Tue, 3 Jun 2014 13:44:37 +0100 Subject: [PATCH 91/95] version increment --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ab55317d..4ee28a2b 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup(name="python-instagram", - version="1.0.1", + version="1.1.0", description="Instagram API client", license="MIT", install_requires=["simplejson","httplib2"], From bf877e8f5ce64319231d6d1b0b2f0be728ca87f7 Mon Sep 17 00:00:00 2001 From: Lucas Carvalho Date: Tue, 3 Jun 2014 18:08:23 -0300 Subject: [PATCH 92/95] supporting /media/shortcode url For more information about this url, check the documentation: - http://instagram.com/developer/endpoints/media/#get_media_by_shortcode --- fixtures/media_shortcode.json | 89 +++++++++++++++++++++++++++++++++++ instagram/client.py | 9 +++- instagram/models.py | 8 ++++ tests.py | 4 ++ 4 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 fixtures/media_shortcode.json diff --git a/fixtures/media_shortcode.json b/fixtures/media_shortcode.json new file mode 100644 index 00000000..47b4708f --- /dev/null +++ b/fixtures/media_shortcode.json @@ -0,0 +1,89 @@ +{ + "meta": { + "code": 200 + }, + "data": { + "attribution": null, + "tags": [ + "alegria" + ], + "type": "image", + "location": null, + "comments": { + "count": 79623, + "data": [] + }, + "filter": "Normal", + "created_time": "1401623658", + "link": "http://instagram.com/p/os1NQjxtvF/", + "likes": { + "count": 370660, + "data": [ + { + "username": "wakamario0904", + "profile_picture": "http://images.ak.instagram.com/profiles/profile_395645793_75sq_1400392412.jpg", + "id": "395645793", + "full_name": "$☆<-W@ka M@rio->☆$" + }, + { + "username": "musaed_ms3", + "profile_picture": "http://images.ak.instagram.com/profiles/profile_1353197338_75sq_1401757650.jpg", + "id": "1353197338", + "full_name": "Musaed" + }, + { + "username": "kerllancritiina_", + "profile_picture": "http://images.ak.instagram.com/profiles/profile_405413316_75sq_1401764697.jpg", + "id": "405413316", + "full_name": "Kerllan Santos" + }, + { + "username": "marccelaaa", + "profile_picture": "http://images.ak.instagram.com/profiles/profile_327540205_75sq_1401046535.jpg", + "id": "327540205", + "full_name": "Marcela Funes" + } + ] + }, + "images": { + "low_resolution": { + "url": "http://scontent-a.cdninstagram.com/hphotos-xpf1/t51.2885-15/10432037_1487631304805008_1552236767_a.jpg", + "width": 306, + "height": 306 + }, + "thumbnail": { + "url": "http://scontent-a.cdninstagram.com/hphotos-xpf1/t51.2885-15/10432037_1487631304805008_1552236767_s.jpg", + "width": 150, + "height": 150 + }, + "standard_resolution": { + "url": "http://scontent-a.cdninstagram.com/hphotos-xpf1/t51.2885-15/10432037_1487631304805008_1552236767_n.jpg", + "width": 640, + "height": 640 + } + }, + "users_in_photo": [], + "caption": { + "created_time": "1401623658", + "text": "Bom diaaaaa !! #alegria #Alegria", + "from": { + "username": "neymarjr", + "profile_picture": "http://images.ak.instagram.com/profiles/profile_26669533_75sq_1396546465.jpg", + "id": "26669533", + "full_name": "Nj" + }, + "id": "733194849033313209" + }, + "user_has_liked": false, + "id": "733194846952938437_26669533", + "user": { + "username": "neymarjr", + "website": "", + "profile_picture": "http://images.ak.instagram.com/profiles/profile_26669533_75sq_1396546465.jpg", + "full_name": "Nj", + "bio": "", + "id": "26669533" + } + } +} + diff --git a/instagram/client.py b/instagram/client.py index f9a2266b..2d67a673 100644 --- a/instagram/client.py +++ b/instagram/client.py @@ -1,6 +1,6 @@ import oauth2 from bind import bind_method -from models import Media, User, Location, Tag, Comment, Relationship +from models import MediaShortcode, Media, User, Location, Tag, Comment, Relationship MEDIA_ACCEPT_PARAMETERS = ["count", "max_id"] SEARCH_ACCEPT_PARAMETERS = ["q", "count"] @@ -38,6 +38,13 @@ def __init__(self, *args, **kwargs): accepts_parameters=SEARCH_ACCEPT_PARAMETERS + ['lat', 'lng', 'min_timestamp', 'max_timestamp', 'distance'], root_class=Media) + media_shortcode = bind_method( + path="/media/shortcode/{shortcode}", + accepts_parameters=['shortcode'], + response_type="entry", + root_class=MediaShortcode) + + media_likes = bind_method( path="/media/{media_id}/likes", accepts_parameters=['media_id'], diff --git a/instagram/models.py b/instagram/models.py index a8394c61..8f73d17a 100644 --- a/instagram/models.py +++ b/instagram/models.py @@ -109,6 +109,14 @@ def object_from_dictionary(cls, entry): return new_media +class MediaShortcode(Media): + + def __init__(self, shortcode=None, **kwargs): + self.shortcode = shortcode + for key, value in kwargs.iteritems(): + setattr(self, key, value) + + class Tag(ApiModel): def __init__(self, name, **kwargs): self.name = name diff --git a/tests.py b/tests.py index 5ca03670..5394a0b5 100755 --- a/tests.py +++ b/tests.py @@ -99,6 +99,10 @@ def test_media_search(self): self.client_only_api.media_search(lat=37.7,lng=-122.22) self.api.media_search(lat=37.7,lng=-122.22) + def test_media_shortcode(self): + self.client_only_api.media_shortcode('os1NQjxtvF') + self.api.media_shortcode('os1NQjxtvF') + def test_media_likes(self): self.client_only_api.media_likes(media_id=4) From 1b9f7a4f95206846c151987d6ad62304cabe53ec Mon Sep 17 00:00:00 2001 From: Jon Heaton Date: Mon, 9 Jun 2014 21:06:54 +0100 Subject: [PATCH 93/95] added media shortcode support --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4ee28a2b..246e181a 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup(name="python-instagram", - version="1.1.0", + version="1.1.1", description="Instagram API client", license="MIT", install_requires=["simplejson","httplib2"], From ada0358ac2abd471d0d8f1a0cb0da00399a7e477 Mon Sep 17 00:00:00 2001 From: gcd0318 Date: Thu, 12 Jun 2014 02:05:18 -0600 Subject: [PATCH 94/95] add python 3 support --- build/lib/instagram/__init__.py | 2 + build/lib/instagram/bind.py | 196 +++++++++++++++ build/lib/instagram/client.py | 237 ++++++++++++++++++ build/lib/instagram/helper.py | 10 + build/lib/instagram/json_import.py | 10 + build/lib/instagram/models.py | 199 +++++++++++++++ build/lib/instagram/oauth2.py | 212 ++++++++++++++++ build/lib/instagram/subscriptions.py | 58 +++++ dist/python_instagram-1.1.1-py3.4.egg | Bin 0 -> 25962 bytes get_access_token.py | 14 +- instagram/__init__.py | 4 +- instagram/bind.py | 16 +- instagram/client.py | 6 +- instagram/models.py | 22 +- instagram/oauth2.py | 12 +- instagram/subscriptions.py | 2 +- python_instagram.egg-info/PKG-INFO | 11 + python_instagram.egg-info/SOURCES.txt | 15 ++ .../dependency_links.txt | 1 + python_instagram.egg-info/requires.txt | 2 + python_instagram.egg-info/top_level.txt | 1 + python_instagram.egg-info/zip-safe | 1 + sample_app.py | 48 ++-- tests.py | 12 +- 24 files changed, 1023 insertions(+), 68 deletions(-) create mode 100644 build/lib/instagram/__init__.py create mode 100644 build/lib/instagram/bind.py create mode 100644 build/lib/instagram/client.py create mode 100644 build/lib/instagram/helper.py create mode 100644 build/lib/instagram/json_import.py create mode 100644 build/lib/instagram/models.py create mode 100644 build/lib/instagram/oauth2.py create mode 100644 build/lib/instagram/subscriptions.py create mode 100644 dist/python_instagram-1.1.1-py3.4.egg mode change 100644 => 100755 get_access_token.py mode change 100644 => 100755 instagram/__init__.py mode change 100644 => 100755 instagram/bind.py mode change 100644 => 100755 instagram/client.py mode change 100644 => 100755 instagram/models.py mode change 100644 => 100755 instagram/oauth2.py mode change 100644 => 100755 instagram/subscriptions.py create mode 100644 python_instagram.egg-info/PKG-INFO create mode 100644 python_instagram.egg-info/SOURCES.txt create mode 100644 python_instagram.egg-info/dependency_links.txt create mode 100644 python_instagram.egg-info/requires.txt create mode 100644 python_instagram.egg-info/top_level.txt create mode 100644 python_instagram.egg-info/zip-safe mode change 100644 => 100755 sample_app.py diff --git a/build/lib/instagram/__init__.py b/build/lib/instagram/__init__.py new file mode 100644 index 00000000..837cc3ef --- /dev/null +++ b/build/lib/instagram/__init__.py @@ -0,0 +1,2 @@ +from .bind import InstagramAPIError, InstagramClientError +from .client import InstagramAPI diff --git a/build/lib/instagram/bind.py b/build/lib/instagram/bind.py new file mode 100644 index 00000000..1934bbea --- /dev/null +++ b/build/lib/instagram/bind.py @@ -0,0 +1,196 @@ +import urllib.request, urllib.parse, urllib.error +from .oauth2 import OAuth2Request +import re +from .json_import import simplejson +import hmac +from hashlib import sha256 + +re_path_template = re.compile('{\w+}') + + +def encode_string(value): + return value.encode('utf-8') \ + if isinstance(value, str) else str(value) + + +class InstagramClientError(Exception): + def __init__(self, error_message, status_code=None): + self.status_code = status_code + self.error_message = error_message + + def __str__(self): + if self.status_code: + return "(%s) %s" % (self.status_code, self.error_message) + else: + return self.error_message + + +class InstagramAPIError(Exception): + + def __init__(self, status_code, error_type, error_message, *args, **kwargs): + self.status_code = status_code + self.error_type = error_type + self.error_message = error_message + + def __str__(self): + return "(%s) %s-%s" % (self.status_code, self.error_type, self.error_message) + + +def bind_method(**config): + + class InstagramAPIMethod(object): + + path = config['path'] + method = config.get('method', 'GET') + accepts_parameters = config.get("accepts_parameters", []) + signature = config.get("signature", False) + requires_target_user = config.get('requires_target_user', False) + paginates = config.get('paginates', False) + root_class = config.get('root_class', None) + response_type = config.get("response_type", "list") + include_secret = config.get("include_secret", False) + objectify_response = config.get("objectify_response", True) + + def __init__(self, api, *args, **kwargs): + self.api = api + self.as_generator = kwargs.pop("as_generator", False) + if self.as_generator: + self.pagination_format = 'next_url' + else: + self.pagination_format = kwargs.pop('pagination_format', 'next_url') + self.return_json = kwargs.pop("return_json", False) + self.max_pages = kwargs.pop("max_pages", 3) + self.with_next_url = kwargs.pop("with_next_url", None) + self.parameters = {} + self._build_parameters(args, kwargs) + self._build_path() + + def _build_parameters(self, args, kwargs): + # via tweepy https://github.com/joshthecoder/tweepy/ + for index, value in enumerate(args): + if value is None: + continue + + try: + self.parameters[self.accepts_parameters[index]] = encode_string(value) + except IndexError: + raise InstagramClientError("Too many arguments supplied") + + for key, value in list(kwargs.items()): + if value is None: + continue + if key in self.parameters: + raise InstagramClientError("Parameter %s already supplied" % key) + self.parameters[key] = encode_string(value) + if 'user_id' in self.accepts_parameters and not 'user_id' in self.parameters \ + and not self.requires_target_user: + self.parameters['user_id'] = 'self' + + def _build_path(self): + for variable in re_path_template.findall(self.path): + name = variable.strip('{}') + + try: + value = urllib.parse.quote(self.parameters[name]) + except KeyError: + raise Exception('No parameter value found for path variable: %s' % name) + del self.parameters[name] + + self.path = self.path.replace(variable, value) + self.path = self.path + '.%s' % self.api.format + + def _build_pagination_info(self, content_obj): + """Extract pagination information in the desired format.""" + pagination = content_obj.get('pagination', {}) + if self.pagination_format == 'next_url': + return pagination.get('next_url') + if self.pagination_format == 'dict': + return pagination + raise Exception('Invalid value for pagination_format: %s' % self.pagination_format) + + def _do_api_request(self, url, method="GET", body=None, headers=None): + headers = headers or {} + if self.signature and self.api.client_ips != None and self.api.client_secret != None: + secret = self.api.client_secret + ips = self.api.client_ips + signature = hmac.new(secret, ips, sha256).hexdigest() + headers['X-Insta-Forwarded-For'] = '|'.join([ips, signature]) + + response, content = OAuth2Request(self.api).make_request(url, method=method, body=body, headers=headers) + if response['status'] == '503' or response['status'] == '429': + raise InstagramAPIError(response['status'], "Rate limited", "Your client is making too many request per second") + + try: + content_obj = simplejson.loads(content) + except ValueError: + raise InstagramClientError('Unable to parse response, not valid JSON.', status_code=response['status']) + + # Handle OAuthRateLimitExceeded from Instagram's Nginx which uses different format to documented api responses + if 'meta' not in content_obj: + if content_obj.get('code') == 420 or content_obj.get('code') == 429: + error_message = content_obj.get('error_message') or "Your client is making too many request per second" + raise InstagramAPIError(content_obj.get('code'), "Rate limited", error_message) + raise InstagramAPIError('code' in content_obj, 'error_type' in content_obj, 'error_message' in content_obj) + + api_responses = [] + status_code = content_obj['meta']['code'] + self.api.x_ratelimit_remaining = response.get("x-ratelimit-remaining",None) + self.api.x_ratelimit = response.get("x-ratelimit-limit",None) + if status_code == 200: + if not self.objectify_response: + return content_obj, None + + if self.response_type == 'list': + for entry in content_obj['data']: + if self.return_json: + api_responses.append(entry) + else: + obj = self.root_class.object_from_dictionary(entry) + api_responses.append(obj) + elif self.response_type == 'entry': + data = content_obj['data'] + if self.return_json: + api_responses = data + else: + api_responses = self.root_class.object_from_dictionary(data) + elif self.response_type == 'empty': + pass + return api_responses, self._build_pagination_info(content_obj) + else: + raise InstagramAPIError(status_code, content_obj['meta']['error_type'], content_obj['meta']['error_message']) + + def _paginator_with_url(self, url, method="GET", body=None, headers=None): + headers = headers or {} + pages_read = 0 + while url and pages_read < self.max_pages: + api_responses, url = self._do_api_request(url, method, body, headers) + pages_read += 1 + yield api_responses, url + return + + def _get_with_next_url(self, url, method="GET", body=None, headers=None): + headers = headers or {} + content, next = self._do_api_request(url, method, body, headers) + return content, next + + def execute(self): + url, method, body, headers = OAuth2Request(self.api).prepare_request(self.method, + self.path, + self.parameters, + include_secret=self.include_secret) + if self.with_next_url: + return self._get_with_next_url(self.with_next_url, method, body, headers) + if self.as_generator: + return self._paginator_with_url(url, method, body, headers) + else: + content, next = self._do_api_request(url, method, body, headers) + if self.paginates: + return content, next + else: + return content + + def _call(api, *args, **kwargs): + method = InstagramAPIMethod(api, *args, **kwargs) + return method.execute() + + return _call diff --git a/build/lib/instagram/client.py b/build/lib/instagram/client.py new file mode 100644 index 00000000..2fcdae7c --- /dev/null +++ b/build/lib/instagram/client.py @@ -0,0 +1,237 @@ +from . import oauth2 +from .bind import bind_method +from .models import MediaShortcode, Media, User, Location, Tag, Comment, Relationship + +MEDIA_ACCEPT_PARAMETERS = ["count", "max_id"] +SEARCH_ACCEPT_PARAMETERS = ["q", "count"] + +SUPPORTED_FORMATS = ['json'] + + +class InstagramAPI(oauth2.OAuth2API): + + host = "api.instagram.com" + base_path = "/v1" + access_token_field = "access_token" + authorize_url = "https://api.instagram.com/oauth/authorize" + access_token_url = "https://api.instagram.com/oauth/access_token" + protocol = "https" + api_name = "Instagram" + x_ratelimit_remaining = None + x_ratelimit = None + + def __init__(self, *args, **kwargs): + format = kwargs.get('format', 'json') + if format in SUPPORTED_FORMATS: + self.format = format + else: + raise Exception("Unsupported format") + super(InstagramAPI, self).__init__(*args, **kwargs) + + media_popular = bind_method( + path="/media/popular", + accepts_parameters=MEDIA_ACCEPT_PARAMETERS, + root_class=Media) + + media_search = bind_method( + path="/media/search", + accepts_parameters=SEARCH_ACCEPT_PARAMETERS + ['lat', 'lng', 'min_timestamp', 'max_timestamp', 'distance'], + root_class=Media) + + media_shortcode = bind_method( + path="/media/shortcode/{shortcode}", + accepts_parameters=['shortcode'], + response_type="entry", + root_class=MediaShortcode) + + + media_likes = bind_method( + path="/media/{media_id}/likes", + accepts_parameters=['media_id'], + root_class=User) + + like_media = bind_method( + path="/media/{media_id}/likes", + method="POST", + signature=True, + accepts_parameters=['media_id'], + response_type="empty") + + unlike_media = bind_method( + path="/media/{media_id}/likes", + method="DELETE", + signature=True, + accepts_parameters=['media_id'], + response_type="empty") + + create_media_comment = bind_method( + path="/media/{media_id}/comments", + method="POST", + signature=True, + accepts_parameters=['media_id', 'text'], + response_type="empty", + root_class=Comment) + + delete_comment = bind_method( + path="/media/{media_id}/comments/{comment_id}", + method="DELETE", + signature=True, + accepts_parameters=['media_id', 'comment_id'], + response_type="empty") + + media_comments = bind_method( + path="/media/{media_id}/comments", + method="GET", + accepts_parameters=['media_id'], + response_type="list", + root_class=Comment) + + media = bind_method( + path="/media/{media_id}", + accepts_parameters=['media_id'], + response_type="entry", + root_class=Media) + + user_media_feed = bind_method( + path="/users/self/feed", + accepts_parameters=MEDIA_ACCEPT_PARAMETERS, + root_class=Media, + paginates=True) + + user_liked_media = bind_method( + path="/users/self/media/liked", + accepts_parameters=MEDIA_ACCEPT_PARAMETERS, + root_class=Media, + paginates=True) + + user_recent_media = bind_method( + path="/users/{user_id}/media/recent", + accepts_parameters=MEDIA_ACCEPT_PARAMETERS + ['user_id'], + root_class=Media, + paginates=True) + + user_search = bind_method( + path="/users/search", + accepts_parameters=SEARCH_ACCEPT_PARAMETERS, + root_class=User) + + user_follows = bind_method( + path="/users/{user_id}/follows", + accepts_parameters=["user_id"], + paginates=True, + root_class=User) + + user_followed_by = bind_method( + path="/users/{user_id}/followed-by", + accepts_parameters=["user_id"], + paginates=True, + root_class=User) + + user = bind_method( + path="/users/{user_id}", + accepts_parameters=["user_id"], + root_class=User, + response_type="entry") + + location_recent_media = bind_method( + path="/locations/{location_id}/media/recent", + accepts_parameters=MEDIA_ACCEPT_PARAMETERS + ['location_id'], + root_class=Media, + paginates=True) + + location_search = bind_method( + path="/locations/search", + accepts_parameters=SEARCH_ACCEPT_PARAMETERS + ['lat', 'lng', 'foursquare_id', 'foursquare_v2_id'], + root_class=Location) + + location = bind_method( + path="/locations/{location_id}", + accepts_parameters=["location_id"], + root_class=Location, + response_type="entry") + + geography_recent_media = bind_method( + path="/geographies/{geography_id}/media/recent", + accepts_parameters=MEDIA_ACCEPT_PARAMETERS + ["geography_id"], + root_class=Media, + paginates=True) + + tag_recent_media = bind_method( + path="/tags/{tag_name}/media/recent", + accepts_parameters=MEDIA_ACCEPT_PARAMETERS + ['tag_name'], + root_class=Media, + paginates=True) + + tag_search = bind_method( + path="/tags/search", + accepts_parameters=SEARCH_ACCEPT_PARAMETERS, + root_class=Tag, + paginates=True) + + tag = bind_method( + path="/tags/{tag_name}", + accepts_parameters=["tag_name"], + root_class=Tag, + response_type="entry") + + user_incoming_requests = bind_method( + path="/users/self/requested-by", + root_class=User) + + change_user_relationship = bind_method( + method="POST", + path="/users/{user_id}/relationship", + signature=True, + root_class=Relationship, + accepts_parameters=["user_id", "action"], + paginates=True, + requires_target_user=True, + response_type="entry") + + user_relationship = bind_method( + method="GET", + path="/users/{user_id}/relationship", + root_class=Relationship, + accepts_parameters=["user_id"], + paginates=False, + requires_target_user=True, + response_type="entry") + + def _make_relationship_shortcut(action): + def _inner(self, *args, **kwargs): + return self.change_user_relationship(user_id=kwargs.get("user_id"), + action=action) + return _inner + + follow_user = _make_relationship_shortcut('follow') + unfollow_user = _make_relationship_shortcut('unfollow') + block_user = _make_relationship_shortcut('block') + unblock_user = _make_relationship_shortcut('unblock') + approve_user_request = _make_relationship_shortcut('approve') + ignore_user_request = _make_relationship_shortcut('ignore') + + def _make_subscription_action(method, include=None, exclude=None): + accepts_parameters = ["object", + "aspect", + "object_id", # Optional if subscribing to all users + "callback_url", + "lat", # Geography + "lng", # Geography + "radius", # Geography + "verify_token"] + + if include: + accepts_parameters.extend(include) + if exclude: + accepts_parameters = [x for x in accepts_parameters if x not in exclude] + return bind_method( + path="/subscriptions", + method=method, + accepts_parameters=accepts_parameters, + include_secret=True, + objectify_response=False + ) + + create_subscription = _make_subscription_action('POST') + list_subscriptions = _make_subscription_action('GET') + delete_subscriptions = _make_subscription_action('DELETE', exclude=['object_id'], include=['id']) diff --git a/build/lib/instagram/helper.py b/build/lib/instagram/helper.py new file mode 100644 index 00000000..62bcf5b5 --- /dev/null +++ b/build/lib/instagram/helper.py @@ -0,0 +1,10 @@ +import calendar +from datetime import datetime + + +def timestamp_to_datetime(ts): + return datetime.utcfromtimestamp(float(ts)) + + +def datetime_to_timestamp(dt): + return calendar.timegm(dt.timetuple()) diff --git a/build/lib/instagram/json_import.py b/build/lib/instagram/json_import.py new file mode 100644 index 00000000..87984799 --- /dev/null +++ b/build/lib/instagram/json_import.py @@ -0,0 +1,10 @@ +try: + import simplejson +except ImportError: + try: + import json as simplejson + except ImportError: + try: + from django.utils import simplejson + except ImportError: + raise ImportError('A json library is required to use this python library') diff --git a/build/lib/instagram/models.py b/build/lib/instagram/models.py new file mode 100644 index 00000000..1c056117 --- /dev/null +++ b/build/lib/instagram/models.py @@ -0,0 +1,199 @@ +from .helper import timestamp_to_datetime + + +class ApiModel(object): + + @classmethod + def object_from_dictionary(cls, entry): + # make dict keys all strings + if entry is None: + return "" + entry_str_dict = dict([(str(key), value) for key, value in list(entry.items())]) + return cls(**entry_str_dict) + + def __repr__(self): + return str(self).encode('utf8') + + +class Image(ApiModel): + + def __init__(self, url, width, height): + self.url = url + self.height = height + self.width = width + + def __unicode__(self): + return "Image: %s" % self.url + + +class Video(Image): + + def __unicode__(self): + return "Video: %s" % self.url + + +class Media(ApiModel): + + def __init__(self, id=None, **kwargs): + self.id = id + for key, value in list(kwargs.items()): + setattr(self, key, value) + + def get_standard_resolution_url(self): + if self.type == 'image': + return self.images['standard_resolution'].url + else: + return self.videos['standard_resolution'].url + + def get_low_resolution_url(self): + if self.type == 'image': + return self.images['low_resolution'].url + else: + return self.videos['low_resolution'].url + + + def get_thumbnail_url(self): + return self.images['thumbnail'].url + + + def __unicode__(self): + return "Media: %s" % self.id + + @classmethod + def object_from_dictionary(cls, entry): + new_media = Media(id=entry['id']) + new_media.type = entry['type'] + + new_media.user = User.object_from_dictionary(entry['user']) + + new_media.images = {} + for version, version_info in list(entry['images'].items()): + new_media.images[version] = Image.object_from_dictionary(version_info) + + if new_media.type == 'video': + new_media.videos = {} + for version, version_info in list(entry['videos'].items()): + new_media.videos[version] = Video.object_from_dictionary(version_info) + + if 'user_has_liked' in entry: + new_media.user_has_liked = entry['user_has_liked'] + new_media.like_count = entry['likes']['count'] + new_media.likes = [] + if 'data' in entry['likes']: + for like in entry['likes']['data']: + new_media.likes.append(User.object_from_dictionary(like)) + + new_media.comment_count = entry['comments']['count'] + new_media.comments = [] + for comment in entry['comments']['data']: + new_media.comments.append(Comment.object_from_dictionary(comment)) + + new_media.created_time = timestamp_to_datetime(entry['created_time']) + + if entry['location'] and 'id' in entry: + new_media.location = Location.object_from_dictionary(entry['location']) + + new_media.caption = None + if entry['caption']: + new_media.caption = Comment.object_from_dictionary(entry['caption']) + + if entry['tags']: + new_media.tags = [] + for tag in entry['tags']: + new_media.tags.append(Tag.object_from_dictionary({'name': tag})) + + new_media.link = entry['link'] + + new_media.filter = entry.get('filter') + + return new_media + + +class MediaShortcode(Media): + + def __init__(self, shortcode=None, **kwargs): + self.shortcode = shortcode + for key, value in list(kwargs.items()): + setattr(self, key, value) + + +class Tag(ApiModel): + def __init__(self, name, **kwargs): + self.name = name + for key, value in list(kwargs.items()): + setattr(self, key, value) + + def __unicode__(self): + return "Tag: %s" % self.name + + +class Comment(ApiModel): + def __init__(self, *args, **kwargs): + for key, value in list(kwargs.items()): + setattr(self, key, value) + + @classmethod + def object_from_dictionary(cls, entry): + user = User.object_from_dictionary(entry['from']) + text = entry['text'] + created_at = timestamp_to_datetime(entry['created_time']) + id = entry['id'] + return Comment(id=id, user=user, text=text, created_at=created_at) + + def __unicode__(self): + return "Comment: %s said \"%s\"" % (self.user.username, self.text) + + +class Point(ApiModel): + def __init__(self, latitude, longitude): + self.latitude = latitude + self.longitude = longitude + + def __unicode__(self): + return "Point: (%s, %s)" % (self.latitude, self.longitude) + + +class Location(ApiModel): + def __init__(self, id, *args, **kwargs): + self.id = id + for key, value in list(kwargs.items()): + setattr(self, key, value) + + @classmethod + def object_from_dictionary(cls, entry): + point = None + if 'latitude' in entry: + point = Point(entry.get('latitude'), + entry.get('longitude')) + location = Location(entry.get('id', 0), + point=point, + name=entry.get('name', '')) + return location + + def __unicode__(self): + return "Location: %s (%s)" % (self.id, self.point) + + +class User(ApiModel): + + def __init__(self, id, *args, **kwargs): + self.id = id + for key, value in list(kwargs.items()): + setattr(self, key, value) + + def __unicode__(self): + return "User: %s" % self.username + + +class Relationship(ApiModel): + + def __init__(self, incoming_status="none", outgoing_status="none", target_user_is_private=False): + self.incoming_status = incoming_status + self.outgoing_status = outgoing_status + self.target_user_is_private = target_user_is_private + + def __unicode__(self): + follows = False if self.outgoing_status == 'none' else True + followed = False if self.incoming_status == 'none' else True + + return "Relationship: (Follows: %s, Followed by: %s)" % (follows, followed) diff --git a/build/lib/instagram/oauth2.py b/build/lib/instagram/oauth2.py new file mode 100644 index 00000000..c8a170ab --- /dev/null +++ b/build/lib/instagram/oauth2.py @@ -0,0 +1,212 @@ +from .json_import import simplejson +import urllib.request, urllib.parse, urllib.error +from httplib2 import Http +import mimetypes + + +class OAuth2AuthExchangeError(Exception): + def __init__(self, description): + self.description = description + + def __str__(self): + return self.description + + +class OAuth2API(object): + host = None + base_path = None + authorize_url = None + access_token_url = None + redirect_uri = None + # some providers use "oauth_token" + access_token_field = "access_token" + protocol = "https" + # override with 'Instagram', etc + api_name = "Generic API" + + def __init__(self, client_id=None, client_secret=None, client_ips=None, access_token=None, redirect_uri=None): + self.client_id = client_id + self.client_secret = client_secret + self.client_ips = client_ips + self.access_token = access_token + self.redirect_uri = redirect_uri + + def get_authorize_url(self, scope=None): + req = OAuth2AuthExchangeRequest(self) + return req.get_authorize_url(scope=scope) + + def get_authorize_login_url(self, scope=None): + """ scope should be a tuple or list of requested scope access levels """ + req = OAuth2AuthExchangeRequest(self) + return req.get_authorize_login_url(scope=scope) + + def exchange_code_for_access_token(self, code): + req = OAuth2AuthExchangeRequest(self) + return req.exchange_for_access_token(code=code) + + def exchange_user_id_for_access_token(self, user_id): + req = OAuth2AuthExchangeRequest(self) + return req.exchange_for_access_token(user_id=user_id) + + def exchange_xauth_login_for_access_token(self, username, password, scope=None): + """ scope should be a tuple or list of requested scope access levels """ + req = OAuth2AuthExchangeRequest(self) + return req.exchange_for_access_token(username=username, password=password, + scope=scope) + + +class OAuth2AuthExchangeRequest(object): + def __init__(self, api): + self.api = api + + def _url_for_authorize(self, scope=None): + client_params = { + "client_id": self.api.client_id, + "response_type": "code", + "redirect_uri": self.api.redirect_uri + } + if scope: + client_params.update(scope=' '.join(scope)) + url_params = urllib.parse.urlencode(client_params) + return "%s?%s" % (self.api.authorize_url, url_params) + + def _data_for_exchange(self, code=None, username=None, password=None, scope=None, user_id=None): + client_params = { + "client_id": self.api.client_id, + "client_secret": self.api.client_secret, + "redirect_uri": self.api.redirect_uri, + "grant_type": "authorization_code" + } + if code: + client_params.update(code=code) + elif username and password: + client_params.update(username=username, + password=password, + grant_type="password") + if scope: + client_params.update(scope=' '.join(scope)) + elif user_id: + client_params.update(user_id=user_id) + return urllib.parse.urlencode(client_params) + + def get_authorize_url(self, scope=None): + return self._url_for_authorize(scope=scope) + + def get_authorize_login_url(self, scope=None): + http_object = Http(disable_ssl_certificate_validation=True) + + url = self._url_for_authorize(scope=scope) + response, content = http_object.request(url) + if response['status'] != '200': + raise OAuth2AuthExchangeError("The server returned a non-200 response for URL %s" % url) + redirected_to = response['content-location'] + return redirected_to + + def exchange_for_access_token(self, code=None, username=None, password=None, scope=None, user_id=None): + data = self._data_for_exchange(code, username, password, scope=scope, user_id=user_id) + http_object = Http(disable_ssl_certificate_validation=True) + url = self.api.access_token_url + response, content = http_object.request(url, method="POST", body=data) + parsed_content = simplejson.loads(content) + if int(response['status']) != 200: + raise OAuth2AuthExchangeError(parsed_content.get("error_message", "")) + return parsed_content['access_token'], parsed_content['user'] + + +class OAuth2Request(object): + def __init__(self, api): + self.api = api + + def url_for_get(self, path, parameters): + return self._full_url_with_params(path, parameters) + + def get_request(self, path, **kwargs): + return self.make_request(self.prepare_request("GET", path, kwargs)) + + def post_request(self, path, **kwargs): + return self.make_request(self.prepare_request("POST", path, kwargs)) + + def _full_url(self, path, include_secret=False): + return "%s://%s%s%s%s" % (self.api.protocol, + self.api.host, + self.api.base_path, + path, + self._auth_query(include_secret)) + + def _full_url_with_params(self, path, params, include_secret=False): + return (self._full_url(path, include_secret) + self._full_query_with_params(params)) + + def _full_query_with_params(self, params): + params = ("&" + urllib.parse.urlencode(params)) if params else "" + return params + + def _auth_query(self, include_secret=False): + if self.api.access_token: + return ("?%s=%s" % (self.api.access_token_field, self.api.access_token)) + elif self.api.client_id: + base = ("?client_id=%s" % (self.api.client_id)) + if include_secret: + base += "&client_secret=%s" % (self.api.client_secret) + return base + + def _post_body(self, params): + return urllib.parse.urlencode(params) + + def _encode_multipart(params, files): + boundary = "MuL7Ip4rt80uND4rYF0o" + + def get_content_type(file_name): + return mimetypes.guess_type(file_name)[0] or "application/octet-stream" + + def encode_field(field_name): + return ("--" + boundary, + 'Content-Disposition: form-data; name="%s"' % (field_name), + "", str(params[field_name])) + + def encode_file(field_name): + file_name, file_handle = files[field_name] + return ("--" + boundary, + 'Content-Disposition: form-data; name="%s"; filename="%s"' % (field_name, file_name), + "Content-Type: " + get_content_type(file_name), + "", file_handle.read()) + + lines = [] + for field in params: + lines.extend(encode_field(field)) + for field in files: + lines.extend(encode_file(field)) + lines.extend(("--%s--" % (boundary), "")) + body = "\r\n".join(lines) + + headers = {"Content-Type": "multipart/form-data; boundary=" + boundary, + "Content-Length": str(len(body))} + + return body, headers + + def prepare_and_make_request(self, method, path, params, include_secret=False): + url, method, body, headers = self.prepare_request(method, path, params, include_secret) + return self.make_request(url, method, body, headers) + + def prepare_request(self, method, path, params, include_secret=False): + url = body = None + headers = {} + + if not params.get('files'): + if method == "POST": + body = self._post_body(params) + headers = {'Content-type': 'application/x-www-form-urlencoded'} + url = self._full_url(path, include_secret) + else: + url = self._full_url_with_params(path, params, include_secret) + else: + body, headers = self._encode_multipart(params, params['files']) + url = self._full_url(path) + + return url, method, body, headers + + def make_request(self, url, method="GET", body=None, headers=None): + headers = headers or {} + if not 'User-Agent' in headers: + headers.update({"User-Agent": "%s Python Client" % self.api.api_name}) + http_obj = Http(disable_ssl_certificate_validation=True) + return http_obj.request(url, method, body=body, headers=headers) diff --git a/build/lib/instagram/subscriptions.py b/build/lib/instagram/subscriptions.py new file mode 100644 index 00000000..227b52bb --- /dev/null +++ b/build/lib/instagram/subscriptions.py @@ -0,0 +1,58 @@ +import hmac +import hashlib +from .json_import import simplejson + +class SubscriptionType: + TAG = 'tag' + USER = 'user' + GEOGRAPHY = 'geography' + LOCATION = 'location' + + +class SubscriptionError(Exception): + pass + + +class SubscriptionVerifyError(SubscriptionError): + pass + + +class SubscriptionsReactor(object): + + callbacks = {} + + def _process_update(self, update): + object_callbacks = self.callbacks.get(update['object'], []) + + for callback in object_callbacks: + callback(update) + + def process(self, client_secret, raw_response, x_hub_signature): + if not self._verify_signature(client_secret, raw_response, x_hub_signature): + raise SubscriptionVerifyError("X-Hub-Signature and hmac digest did not match") + + try: + response = simplejson.loads(raw_response) + except ValueError: + raise SubscriptionError('Unable to parse response, not valid JSON.') + + for update in response: + self._process_update(update) + + def register_callback(self, object_type, callback): + cb_list = self.callbacks.get(object_type, []) + + if callback not in cb_list: + cb_list.append(callback) + self.callbacks[object_type] = cb_list + + def deregister_callback(self, object_type, callback): + callbacks = self.callbacks.get(object_type, []) + callbacks.remove(callback) + + def _verify_signature(self, client_secret, raw_response, x_hub_signature): + digest = hmac.new(client_secret.encode('utf-8'), + msg=raw_response.encode('utf-8'), + digestmod=hashlib.sha1 + ).hexdigest() + return digest == x_hub_signature diff --git a/dist/python_instagram-1.1.1-py3.4.egg b/dist/python_instagram-1.1.1-py3.4.egg new file mode 100644 index 0000000000000000000000000000000000000000..f8662a5ac60c4047c59bb06bd6352ba1494350a3 GIT binary patch literal 25962 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 with Instagram' % url - except Exception, e: - print e + except Exception as e: + print(e) def get_nav(): nav_menu = ("

    Python Instagram

    " @@ -53,13 +53,13 @@ def on_callback(session): return 'Missing code' try: access_token, user_info = unauthenticated_api.exchange_code_for_access_token(code) - print "access token= " + access_token + print(("access token= " + access_token)) if not access_token: return 'Could not get access token' api = client.InstagramAPI(access_token=access_token) session['access_token']=access_token - except Exception, e: - print e + except Exception as e: + print(e) return get_nav() @route('/recent') @@ -78,11 +78,11 @@ def on_recent(session): photos.append('' % (media.get_standard_resolution_url())) else: photos.append('' % (media.get_low_resolution_url())) - print media + print(media) photos.append("
    Like Un-Like LikesCount=%s" % (media.id,media.id,media.like_count)) content += ''.join(photos) - except Exception, e: - print e + except Exception as e: + print(e) return "%s %s
    Remaining API Calls = %s/%s" % (get_nav(),content,api.x_ratelimit_remaining,api.x_ratelimit) @route('/media_like/') @@ -118,8 +118,8 @@ def on_user_media_feed(session): photos.append('' % media.get_standard_resolution_url()) counter += 1 content += ''.join(photos) - except Exception, e: - print e + except Exception as e: + print(e) return "%s %s
    Remaining API Calls = %s/%s" % (get_nav(),content,api.x_ratelimit_remaining,api.x_ratelimit) @route('/location_recent_media') @@ -135,8 +135,8 @@ def location_recent_media(session): for media in recent_media: photos.append('' % media.get_standard_resolution_url()) content += ''.join(photos) - except Exception, e: - print e + except Exception as e: + print(e) return "%s %s
    Remaining API Calls = %s/%s" % (get_nav(),content,api.x_ratelimit_remaining,api.x_ratelimit) @route('/media_search') @@ -152,8 +152,8 @@ def media_search(session): for media in media_search: photos.append('' % media.get_standard_resolution_url()) content += ''.join(photos) - except Exception, e: - print e + except Exception as e: + print(e) return "%s %s
    Remaining API Calls = %s/%s" % (get_nav(),content,api.x_ratelimit_remaining,api.x_ratelimit) @route('/media_popular') @@ -169,8 +169,8 @@ def media_popular(session): for media in media_search: photos.append('' % media.get_standard_resolution_url()) content += ''.join(photos) - except Exception, e: - print e + except Exception as e: + print(e) return "%s %s
    Remaining API Calls = %s/%s" % (get_nav(),content,api.x_ratelimit_remaining,api.x_ratelimit) @route('/user_search') @@ -186,8 +186,8 @@ def user_search(session): for user in user_search: users.append('
  • %s
  • ' % (user.profile_picture,user.username)) content += ''.join(users) - except Exception, e: - print e + except Exception as e: + print(e) return "%s %s
    Remaining API Calls = %s/%s" % (get_nav(),content,api.x_ratelimit_remaining,api.x_ratelimit) @route('/location_search') @@ -203,8 +203,8 @@ def location_search(session): for location in location_search: locations.append('
  • %s Map
  • ' % (location.name,location.point.latitude,location.point.longitude)) content += ''.join(locations) - except Exception, e: - print e + except Exception as e: + print(e) return "%s %s
    Remaining API Calls = %s/%s" % (get_nav(),content,api.x_ratelimit_remaining,api.x_ratelimit) @route('/tag_search') @@ -221,8 +221,8 @@ def tag_search(session): for tag_media in tag_recent_media: photos.append('' % tag_media.get_standard_resolution_url()) content += ''.join(photos) - except Exception, e: - print e + except Exception as e: + print(e) return "%s %s
    Remaining API Calls = %s/%s" % (get_nav(),content,api.x_ratelimit_remaining,api.x_ratelimit) @route('/realtime_callback') @@ -239,6 +239,6 @@ def on_realtime_callback(): try: reactor.process(CONFIG['client_secret'], raw_response, x_hub_signature) except subscriptions.SubscriptionVerifyError: - print "Signature mismatch" + print("Signature mismatch") run(host='localhost', port=8515, reloader=True) \ No newline at end of file diff --git a/tests.py b/tests.py index 5394a0b5..5f265208 100755 --- a/tests.py +++ b/tests.py @@ -7,7 +7,7 @@ import json import getpass import unittest -import urlparse +import urllib.parse from instagram import client, oauth2, InstagramAPIError TEST_AUTH = False @@ -26,8 +26,8 @@ def request(self, url, method="GET", body=None, headers={}): 'status':'400' }, "{}" - parsed = urlparse.urlparse(url) - options = urlparse.parse_qs(parsed.query) + parsed = urllib.parse.urlparse(url) + options = urllib.parse.parse_qs(parsed.query) fn_name = str(active_call) if fn_name == 'get_authorize_login_url': @@ -67,8 +67,8 @@ def setUp(self): def test_authorize_login_url(self): redirect_uri = self.unauthenticated_api.get_authorize_login_url() assert redirect_uri - print "Please visit and authorize at:\n%s" % redirect_uri - code = raw_input("Paste received code (blank to skip): ").strip() + print(("Please visit and authorize at:\n%s" % redirect_uri)) + code = input("Paste received code (blank to skip): ").strip() if not code: return @@ -78,7 +78,7 @@ def test_authorize_login_url(self): def test_xauth_exchange(self): """ Your client ID must be authorized for xAuth access; email xauth@instagram.com for access""" - username = raw_input("Enter username for XAuth (blank to skip): ").strip() + username = input("Enter username for XAuth (blank to skip): ").strip() if not username: return password = getpass.getpass("Enter password for XAuth (blank to skip): ").strip() From dab0861c724f72d3a503bfae772fab50f2cebe8d Mon Sep 17 00:00:00 2001 From: gcd0318 Date: Thu, 12 Jun 2014 02:07:24 -0600 Subject: [PATCH 95/95] rm build files, README modified for python 3 --- README.md | 2 +- build/lib/instagram/__init__.py | 2 - build/lib/instagram/bind.py | 196 --------------- build/lib/instagram/client.py | 237 ------------------ build/lib/instagram/helper.py | 10 - build/lib/instagram/json_import.py | 10 - build/lib/instagram/models.py | 199 --------------- build/lib/instagram/oauth2.py | 212 ---------------- build/lib/instagram/subscriptions.py | 58 ----- dist/python_instagram-1.1.1-py3.4.egg | Bin 25962 -> 0 bytes python_instagram.egg-info/PKG-INFO | 11 - python_instagram.egg-info/SOURCES.txt | 15 -- .../dependency_links.txt | 1 - python_instagram.egg-info/requires.txt | 2 - python_instagram.egg-info/top_level.txt | 1 - python_instagram.egg-info/zip-safe | 1 - 16 files changed, 1 insertion(+), 956 deletions(-) delete mode 100644 build/lib/instagram/__init__.py delete mode 100644 build/lib/instagram/bind.py delete mode 100644 build/lib/instagram/client.py delete mode 100644 build/lib/instagram/helper.py delete mode 100644 build/lib/instagram/json_import.py delete mode 100644 build/lib/instagram/models.py delete mode 100644 build/lib/instagram/oauth2.py delete mode 100644 build/lib/instagram/subscriptions.py delete mode 100644 dist/python_instagram-1.1.1-py3.4.egg delete mode 100644 python_instagram.egg-info/PKG-INFO delete mode 100644 python_instagram.egg-info/SOURCES.txt delete mode 100644 python_instagram.egg-info/dependency_links.txt delete mode 100644 python_instagram.egg-info/requires.txt delete mode 100644 python_instagram.egg-info/top_level.txt delete mode 100644 python_instagram.egg-info/zip-safe diff --git a/README.md b/README.md index 597d6976..0988c19d 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ constructor: api = InstagramAPI(client_id='YOUR_CLIENT_ID', client_secret='YOUR_CLIENT_SECRET') popular_media = api.media_popular(count=20) for media in popular_media: - print media.images['standard_resolution'].url + print(media.images['standard_resolution'].url) ``` Real-time Subscriptions: diff --git a/build/lib/instagram/__init__.py b/build/lib/instagram/__init__.py deleted file mode 100644 index 837cc3ef..00000000 --- a/build/lib/instagram/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .bind import InstagramAPIError, InstagramClientError -from .client import InstagramAPI diff --git a/build/lib/instagram/bind.py b/build/lib/instagram/bind.py deleted file mode 100644 index 1934bbea..00000000 --- a/build/lib/instagram/bind.py +++ /dev/null @@ -1,196 +0,0 @@ -import urllib.request, urllib.parse, urllib.error -from .oauth2 import OAuth2Request -import re -from .json_import import simplejson -import hmac -from hashlib import sha256 - -re_path_template = re.compile('{\w+}') - - -def encode_string(value): - return value.encode('utf-8') \ - if isinstance(value, str) else str(value) - - -class InstagramClientError(Exception): - def __init__(self, error_message, status_code=None): - self.status_code = status_code - self.error_message = error_message - - def __str__(self): - if self.status_code: - return "(%s) %s" % (self.status_code, self.error_message) - else: - return self.error_message - - -class InstagramAPIError(Exception): - - def __init__(self, status_code, error_type, error_message, *args, **kwargs): - self.status_code = status_code - self.error_type = error_type - self.error_message = error_message - - def __str__(self): - return "(%s) %s-%s" % (self.status_code, self.error_type, self.error_message) - - -def bind_method(**config): - - class InstagramAPIMethod(object): - - path = config['path'] - method = config.get('method', 'GET') - accepts_parameters = config.get("accepts_parameters", []) - signature = config.get("signature", False) - requires_target_user = config.get('requires_target_user', False) - paginates = config.get('paginates', False) - root_class = config.get('root_class', None) - response_type = config.get("response_type", "list") - include_secret = config.get("include_secret", False) - objectify_response = config.get("objectify_response", True) - - def __init__(self, api, *args, **kwargs): - self.api = api - self.as_generator = kwargs.pop("as_generator", False) - if self.as_generator: - self.pagination_format = 'next_url' - else: - self.pagination_format = kwargs.pop('pagination_format', 'next_url') - self.return_json = kwargs.pop("return_json", False) - self.max_pages = kwargs.pop("max_pages", 3) - self.with_next_url = kwargs.pop("with_next_url", None) - self.parameters = {} - self._build_parameters(args, kwargs) - self._build_path() - - def _build_parameters(self, args, kwargs): - # via tweepy https://github.com/joshthecoder/tweepy/ - for index, value in enumerate(args): - if value is None: - continue - - try: - self.parameters[self.accepts_parameters[index]] = encode_string(value) - except IndexError: - raise InstagramClientError("Too many arguments supplied") - - for key, value in list(kwargs.items()): - if value is None: - continue - if key in self.parameters: - raise InstagramClientError("Parameter %s already supplied" % key) - self.parameters[key] = encode_string(value) - if 'user_id' in self.accepts_parameters and not 'user_id' in self.parameters \ - and not self.requires_target_user: - self.parameters['user_id'] = 'self' - - def _build_path(self): - for variable in re_path_template.findall(self.path): - name = variable.strip('{}') - - try: - value = urllib.parse.quote(self.parameters[name]) - except KeyError: - raise Exception('No parameter value found for path variable: %s' % name) - del self.parameters[name] - - self.path = self.path.replace(variable, value) - self.path = self.path + '.%s' % self.api.format - - def _build_pagination_info(self, content_obj): - """Extract pagination information in the desired format.""" - pagination = content_obj.get('pagination', {}) - if self.pagination_format == 'next_url': - return pagination.get('next_url') - if self.pagination_format == 'dict': - return pagination - raise Exception('Invalid value for pagination_format: %s' % self.pagination_format) - - def _do_api_request(self, url, method="GET", body=None, headers=None): - headers = headers or {} - if self.signature and self.api.client_ips != None and self.api.client_secret != None: - secret = self.api.client_secret - ips = self.api.client_ips - signature = hmac.new(secret, ips, sha256).hexdigest() - headers['X-Insta-Forwarded-For'] = '|'.join([ips, signature]) - - response, content = OAuth2Request(self.api).make_request(url, method=method, body=body, headers=headers) - if response['status'] == '503' or response['status'] == '429': - raise InstagramAPIError(response['status'], "Rate limited", "Your client is making too many request per second") - - try: - content_obj = simplejson.loads(content) - except ValueError: - raise InstagramClientError('Unable to parse response, not valid JSON.', status_code=response['status']) - - # Handle OAuthRateLimitExceeded from Instagram's Nginx which uses different format to documented api responses - if 'meta' not in content_obj: - if content_obj.get('code') == 420 or content_obj.get('code') == 429: - error_message = content_obj.get('error_message') or "Your client is making too many request per second" - raise InstagramAPIError(content_obj.get('code'), "Rate limited", error_message) - raise InstagramAPIError('code' in content_obj, 'error_type' in content_obj, 'error_message' in content_obj) - - api_responses = [] - status_code = content_obj['meta']['code'] - self.api.x_ratelimit_remaining = response.get("x-ratelimit-remaining",None) - self.api.x_ratelimit = response.get("x-ratelimit-limit",None) - if status_code == 200: - if not self.objectify_response: - return content_obj, None - - if self.response_type == 'list': - for entry in content_obj['data']: - if self.return_json: - api_responses.append(entry) - else: - obj = self.root_class.object_from_dictionary(entry) - api_responses.append(obj) - elif self.response_type == 'entry': - data = content_obj['data'] - if self.return_json: - api_responses = data - else: - api_responses = self.root_class.object_from_dictionary(data) - elif self.response_type == 'empty': - pass - return api_responses, self._build_pagination_info(content_obj) - else: - raise InstagramAPIError(status_code, content_obj['meta']['error_type'], content_obj['meta']['error_message']) - - def _paginator_with_url(self, url, method="GET", body=None, headers=None): - headers = headers or {} - pages_read = 0 - while url and pages_read < self.max_pages: - api_responses, url = self._do_api_request(url, method, body, headers) - pages_read += 1 - yield api_responses, url - return - - def _get_with_next_url(self, url, method="GET", body=None, headers=None): - headers = headers or {} - content, next = self._do_api_request(url, method, body, headers) - return content, next - - def execute(self): - url, method, body, headers = OAuth2Request(self.api).prepare_request(self.method, - self.path, - self.parameters, - include_secret=self.include_secret) - if self.with_next_url: - return self._get_with_next_url(self.with_next_url, method, body, headers) - if self.as_generator: - return self._paginator_with_url(url, method, body, headers) - else: - content, next = self._do_api_request(url, method, body, headers) - if self.paginates: - return content, next - else: - return content - - def _call(api, *args, **kwargs): - method = InstagramAPIMethod(api, *args, **kwargs) - return method.execute() - - return _call diff --git a/build/lib/instagram/client.py b/build/lib/instagram/client.py deleted file mode 100644 index 2fcdae7c..00000000 --- a/build/lib/instagram/client.py +++ /dev/null @@ -1,237 +0,0 @@ -from . import oauth2 -from .bind import bind_method -from .models import MediaShortcode, Media, User, Location, Tag, Comment, Relationship - -MEDIA_ACCEPT_PARAMETERS = ["count", "max_id"] -SEARCH_ACCEPT_PARAMETERS = ["q", "count"] - -SUPPORTED_FORMATS = ['json'] - - -class InstagramAPI(oauth2.OAuth2API): - - host = "api.instagram.com" - base_path = "/v1" - access_token_field = "access_token" - authorize_url = "https://api.instagram.com/oauth/authorize" - access_token_url = "https://api.instagram.com/oauth/access_token" - protocol = "https" - api_name = "Instagram" - x_ratelimit_remaining = None - x_ratelimit = None - - def __init__(self, *args, **kwargs): - format = kwargs.get('format', 'json') - if format in SUPPORTED_FORMATS: - self.format = format - else: - raise Exception("Unsupported format") - super(InstagramAPI, self).__init__(*args, **kwargs) - - media_popular = bind_method( - path="/media/popular", - accepts_parameters=MEDIA_ACCEPT_PARAMETERS, - root_class=Media) - - media_search = bind_method( - path="/media/search", - accepts_parameters=SEARCH_ACCEPT_PARAMETERS + ['lat', 'lng', 'min_timestamp', 'max_timestamp', 'distance'], - root_class=Media) - - media_shortcode = bind_method( - path="/media/shortcode/{shortcode}", - accepts_parameters=['shortcode'], - response_type="entry", - root_class=MediaShortcode) - - - media_likes = bind_method( - path="/media/{media_id}/likes", - accepts_parameters=['media_id'], - root_class=User) - - like_media = bind_method( - path="/media/{media_id}/likes", - method="POST", - signature=True, - accepts_parameters=['media_id'], - response_type="empty") - - unlike_media = bind_method( - path="/media/{media_id}/likes", - method="DELETE", - signature=True, - accepts_parameters=['media_id'], - response_type="empty") - - create_media_comment = bind_method( - path="/media/{media_id}/comments", - method="POST", - signature=True, - accepts_parameters=['media_id', 'text'], - response_type="empty", - root_class=Comment) - - delete_comment = bind_method( - path="/media/{media_id}/comments/{comment_id}", - method="DELETE", - signature=True, - accepts_parameters=['media_id', 'comment_id'], - response_type="empty") - - media_comments = bind_method( - path="/media/{media_id}/comments", - method="GET", - accepts_parameters=['media_id'], - response_type="list", - root_class=Comment) - - media = bind_method( - path="/media/{media_id}", - accepts_parameters=['media_id'], - response_type="entry", - root_class=Media) - - user_media_feed = bind_method( - path="/users/self/feed", - accepts_parameters=MEDIA_ACCEPT_PARAMETERS, - root_class=Media, - paginates=True) - - user_liked_media = bind_method( - path="/users/self/media/liked", - accepts_parameters=MEDIA_ACCEPT_PARAMETERS, - root_class=Media, - paginates=True) - - user_recent_media = bind_method( - path="/users/{user_id}/media/recent", - accepts_parameters=MEDIA_ACCEPT_PARAMETERS + ['user_id'], - root_class=Media, - paginates=True) - - user_search = bind_method( - path="/users/search", - accepts_parameters=SEARCH_ACCEPT_PARAMETERS, - root_class=User) - - user_follows = bind_method( - path="/users/{user_id}/follows", - accepts_parameters=["user_id"], - paginates=True, - root_class=User) - - user_followed_by = bind_method( - path="/users/{user_id}/followed-by", - accepts_parameters=["user_id"], - paginates=True, - root_class=User) - - user = bind_method( - path="/users/{user_id}", - accepts_parameters=["user_id"], - root_class=User, - response_type="entry") - - location_recent_media = bind_method( - path="/locations/{location_id}/media/recent", - accepts_parameters=MEDIA_ACCEPT_PARAMETERS + ['location_id'], - root_class=Media, - paginates=True) - - location_search = bind_method( - path="/locations/search", - accepts_parameters=SEARCH_ACCEPT_PARAMETERS + ['lat', 'lng', 'foursquare_id', 'foursquare_v2_id'], - root_class=Location) - - location = bind_method( - path="/locations/{location_id}", - accepts_parameters=["location_id"], - root_class=Location, - response_type="entry") - - geography_recent_media = bind_method( - path="/geographies/{geography_id}/media/recent", - accepts_parameters=MEDIA_ACCEPT_PARAMETERS + ["geography_id"], - root_class=Media, - paginates=True) - - tag_recent_media = bind_method( - path="/tags/{tag_name}/media/recent", - accepts_parameters=MEDIA_ACCEPT_PARAMETERS + ['tag_name'], - root_class=Media, - paginates=True) - - tag_search = bind_method( - path="/tags/search", - accepts_parameters=SEARCH_ACCEPT_PARAMETERS, - root_class=Tag, - paginates=True) - - tag = bind_method( - path="/tags/{tag_name}", - accepts_parameters=["tag_name"], - root_class=Tag, - response_type="entry") - - user_incoming_requests = bind_method( - path="/users/self/requested-by", - root_class=User) - - change_user_relationship = bind_method( - method="POST", - path="/users/{user_id}/relationship", - signature=True, - root_class=Relationship, - accepts_parameters=["user_id", "action"], - paginates=True, - requires_target_user=True, - response_type="entry") - - user_relationship = bind_method( - method="GET", - path="/users/{user_id}/relationship", - root_class=Relationship, - accepts_parameters=["user_id"], - paginates=False, - requires_target_user=True, - response_type="entry") - - def _make_relationship_shortcut(action): - def _inner(self, *args, **kwargs): - return self.change_user_relationship(user_id=kwargs.get("user_id"), - action=action) - return _inner - - follow_user = _make_relationship_shortcut('follow') - unfollow_user = _make_relationship_shortcut('unfollow') - block_user = _make_relationship_shortcut('block') - unblock_user = _make_relationship_shortcut('unblock') - approve_user_request = _make_relationship_shortcut('approve') - ignore_user_request = _make_relationship_shortcut('ignore') - - def _make_subscription_action(method, include=None, exclude=None): - accepts_parameters = ["object", - "aspect", - "object_id", # Optional if subscribing to all users - "callback_url", - "lat", # Geography - "lng", # Geography - "radius", # Geography - "verify_token"] - - if include: - accepts_parameters.extend(include) - if exclude: - accepts_parameters = [x for x in accepts_parameters if x not in exclude] - return bind_method( - path="/subscriptions", - method=method, - accepts_parameters=accepts_parameters, - include_secret=True, - objectify_response=False - ) - - create_subscription = _make_subscription_action('POST') - list_subscriptions = _make_subscription_action('GET') - delete_subscriptions = _make_subscription_action('DELETE', exclude=['object_id'], include=['id']) diff --git a/build/lib/instagram/helper.py b/build/lib/instagram/helper.py deleted file mode 100644 index 62bcf5b5..00000000 --- a/build/lib/instagram/helper.py +++ /dev/null @@ -1,10 +0,0 @@ -import calendar -from datetime import datetime - - -def timestamp_to_datetime(ts): - return datetime.utcfromtimestamp(float(ts)) - - -def datetime_to_timestamp(dt): - return calendar.timegm(dt.timetuple()) diff --git a/build/lib/instagram/json_import.py b/build/lib/instagram/json_import.py deleted file mode 100644 index 87984799..00000000 --- a/build/lib/instagram/json_import.py +++ /dev/null @@ -1,10 +0,0 @@ -try: - import simplejson -except ImportError: - try: - import json as simplejson - except ImportError: - try: - from django.utils import simplejson - except ImportError: - raise ImportError('A json library is required to use this python library') diff --git a/build/lib/instagram/models.py b/build/lib/instagram/models.py deleted file mode 100644 index 1c056117..00000000 --- a/build/lib/instagram/models.py +++ /dev/null @@ -1,199 +0,0 @@ -from .helper import timestamp_to_datetime - - -class ApiModel(object): - - @classmethod - def object_from_dictionary(cls, entry): - # make dict keys all strings - if entry is None: - return "" - entry_str_dict = dict([(str(key), value) for key, value in list(entry.items())]) - return cls(**entry_str_dict) - - def __repr__(self): - return str(self).encode('utf8') - - -class Image(ApiModel): - - def __init__(self, url, width, height): - self.url = url - self.height = height - self.width = width - - def __unicode__(self): - return "Image: %s" % self.url - - -class Video(Image): - - def __unicode__(self): - return "Video: %s" % self.url - - -class Media(ApiModel): - - def __init__(self, id=None, **kwargs): - self.id = id - for key, value in list(kwargs.items()): - setattr(self, key, value) - - def get_standard_resolution_url(self): - if self.type == 'image': - return self.images['standard_resolution'].url - else: - return self.videos['standard_resolution'].url - - def get_low_resolution_url(self): - if self.type == 'image': - return self.images['low_resolution'].url - else: - return self.videos['low_resolution'].url - - - def get_thumbnail_url(self): - return self.images['thumbnail'].url - - - def __unicode__(self): - return "Media: %s" % self.id - - @classmethod - def object_from_dictionary(cls, entry): - new_media = Media(id=entry['id']) - new_media.type = entry['type'] - - new_media.user = User.object_from_dictionary(entry['user']) - - new_media.images = {} - for version, version_info in list(entry['images'].items()): - new_media.images[version] = Image.object_from_dictionary(version_info) - - if new_media.type == 'video': - new_media.videos = {} - for version, version_info in list(entry['videos'].items()): - new_media.videos[version] = Video.object_from_dictionary(version_info) - - if 'user_has_liked' in entry: - new_media.user_has_liked = entry['user_has_liked'] - new_media.like_count = entry['likes']['count'] - new_media.likes = [] - if 'data' in entry['likes']: - for like in entry['likes']['data']: - new_media.likes.append(User.object_from_dictionary(like)) - - new_media.comment_count = entry['comments']['count'] - new_media.comments = [] - for comment in entry['comments']['data']: - new_media.comments.append(Comment.object_from_dictionary(comment)) - - new_media.created_time = timestamp_to_datetime(entry['created_time']) - - if entry['location'] and 'id' in entry: - new_media.location = Location.object_from_dictionary(entry['location']) - - new_media.caption = None - if entry['caption']: - new_media.caption = Comment.object_from_dictionary(entry['caption']) - - if entry['tags']: - new_media.tags = [] - for tag in entry['tags']: - new_media.tags.append(Tag.object_from_dictionary({'name': tag})) - - new_media.link = entry['link'] - - new_media.filter = entry.get('filter') - - return new_media - - -class MediaShortcode(Media): - - def __init__(self, shortcode=None, **kwargs): - self.shortcode = shortcode - for key, value in list(kwargs.items()): - setattr(self, key, value) - - -class Tag(ApiModel): - def __init__(self, name, **kwargs): - self.name = name - for key, value in list(kwargs.items()): - setattr(self, key, value) - - def __unicode__(self): - return "Tag: %s" % self.name - - -class Comment(ApiModel): - def __init__(self, *args, **kwargs): - for key, value in list(kwargs.items()): - setattr(self, key, value) - - @classmethod - def object_from_dictionary(cls, entry): - user = User.object_from_dictionary(entry['from']) - text = entry['text'] - created_at = timestamp_to_datetime(entry['created_time']) - id = entry['id'] - return Comment(id=id, user=user, text=text, created_at=created_at) - - def __unicode__(self): - return "Comment: %s said \"%s\"" % (self.user.username, self.text) - - -class Point(ApiModel): - def __init__(self, latitude, longitude): - self.latitude = latitude - self.longitude = longitude - - def __unicode__(self): - return "Point: (%s, %s)" % (self.latitude, self.longitude) - - -class Location(ApiModel): - def __init__(self, id, *args, **kwargs): - self.id = id - for key, value in list(kwargs.items()): - setattr(self, key, value) - - @classmethod - def object_from_dictionary(cls, entry): - point = None - if 'latitude' in entry: - point = Point(entry.get('latitude'), - entry.get('longitude')) - location = Location(entry.get('id', 0), - point=point, - name=entry.get('name', '')) - return location - - def __unicode__(self): - return "Location: %s (%s)" % (self.id, self.point) - - -class User(ApiModel): - - def __init__(self, id, *args, **kwargs): - self.id = id - for key, value in list(kwargs.items()): - setattr(self, key, value) - - def __unicode__(self): - return "User: %s" % self.username - - -class Relationship(ApiModel): - - def __init__(self, incoming_status="none", outgoing_status="none", target_user_is_private=False): - self.incoming_status = incoming_status - self.outgoing_status = outgoing_status - self.target_user_is_private = target_user_is_private - - def __unicode__(self): - follows = False if self.outgoing_status == 'none' else True - followed = False if self.incoming_status == 'none' else True - - return "Relationship: (Follows: %s, Followed by: %s)" % (follows, followed) diff --git a/build/lib/instagram/oauth2.py b/build/lib/instagram/oauth2.py deleted file mode 100644 index c8a170ab..00000000 --- a/build/lib/instagram/oauth2.py +++ /dev/null @@ -1,212 +0,0 @@ -from .json_import import simplejson -import urllib.request, urllib.parse, urllib.error -from httplib2 import Http -import mimetypes - - -class OAuth2AuthExchangeError(Exception): - def __init__(self, description): - self.description = description - - def __str__(self): - return self.description - - -class OAuth2API(object): - host = None - base_path = None - authorize_url = None - access_token_url = None - redirect_uri = None - # some providers use "oauth_token" - access_token_field = "access_token" - protocol = "https" - # override with 'Instagram', etc - api_name = "Generic API" - - def __init__(self, client_id=None, client_secret=None, client_ips=None, access_token=None, redirect_uri=None): - self.client_id = client_id - self.client_secret = client_secret - self.client_ips = client_ips - self.access_token = access_token - self.redirect_uri = redirect_uri - - def get_authorize_url(self, scope=None): - req = OAuth2AuthExchangeRequest(self) - return req.get_authorize_url(scope=scope) - - def get_authorize_login_url(self, scope=None): - """ scope should be a tuple or list of requested scope access levels """ - req = OAuth2AuthExchangeRequest(self) - return req.get_authorize_login_url(scope=scope) - - def exchange_code_for_access_token(self, code): - req = OAuth2AuthExchangeRequest(self) - return req.exchange_for_access_token(code=code) - - def exchange_user_id_for_access_token(self, user_id): - req = OAuth2AuthExchangeRequest(self) - return req.exchange_for_access_token(user_id=user_id) - - def exchange_xauth_login_for_access_token(self, username, password, scope=None): - """ scope should be a tuple or list of requested scope access levels """ - req = OAuth2AuthExchangeRequest(self) - return req.exchange_for_access_token(username=username, password=password, - scope=scope) - - -class OAuth2AuthExchangeRequest(object): - def __init__(self, api): - self.api = api - - def _url_for_authorize(self, scope=None): - client_params = { - "client_id": self.api.client_id, - "response_type": "code", - "redirect_uri": self.api.redirect_uri - } - if scope: - client_params.update(scope=' '.join(scope)) - url_params = urllib.parse.urlencode(client_params) - return "%s?%s" % (self.api.authorize_url, url_params) - - def _data_for_exchange(self, code=None, username=None, password=None, scope=None, user_id=None): - client_params = { - "client_id": self.api.client_id, - "client_secret": self.api.client_secret, - "redirect_uri": self.api.redirect_uri, - "grant_type": "authorization_code" - } - if code: - client_params.update(code=code) - elif username and password: - client_params.update(username=username, - password=password, - grant_type="password") - if scope: - client_params.update(scope=' '.join(scope)) - elif user_id: - client_params.update(user_id=user_id) - return urllib.parse.urlencode(client_params) - - def get_authorize_url(self, scope=None): - return self._url_for_authorize(scope=scope) - - def get_authorize_login_url(self, scope=None): - http_object = Http(disable_ssl_certificate_validation=True) - - url = self._url_for_authorize(scope=scope) - response, content = http_object.request(url) - if response['status'] != '200': - raise OAuth2AuthExchangeError("The server returned a non-200 response for URL %s" % url) - redirected_to = response['content-location'] - return redirected_to - - def exchange_for_access_token(self, code=None, username=None, password=None, scope=None, user_id=None): - data = self._data_for_exchange(code, username, password, scope=scope, user_id=user_id) - http_object = Http(disable_ssl_certificate_validation=True) - url = self.api.access_token_url - response, content = http_object.request(url, method="POST", body=data) - parsed_content = simplejson.loads(content) - if int(response['status']) != 200: - raise OAuth2AuthExchangeError(parsed_content.get("error_message", "")) - return parsed_content['access_token'], parsed_content['user'] - - -class OAuth2Request(object): - def __init__(self, api): - self.api = api - - def url_for_get(self, path, parameters): - return self._full_url_with_params(path, parameters) - - def get_request(self, path, **kwargs): - return self.make_request(self.prepare_request("GET", path, kwargs)) - - def post_request(self, path, **kwargs): - return self.make_request(self.prepare_request("POST", path, kwargs)) - - def _full_url(self, path, include_secret=False): - return "%s://%s%s%s%s" % (self.api.protocol, - self.api.host, - self.api.base_path, - path, - self._auth_query(include_secret)) - - def _full_url_with_params(self, path, params, include_secret=False): - return (self._full_url(path, include_secret) + self._full_query_with_params(params)) - - def _full_query_with_params(self, params): - params = ("&" + urllib.parse.urlencode(params)) if params else "" - return params - - def _auth_query(self, include_secret=False): - if self.api.access_token: - return ("?%s=%s" % (self.api.access_token_field, self.api.access_token)) - elif self.api.client_id: - base = ("?client_id=%s" % (self.api.client_id)) - if include_secret: - base += "&client_secret=%s" % (self.api.client_secret) - return base - - def _post_body(self, params): - return urllib.parse.urlencode(params) - - def _encode_multipart(params, files): - boundary = "MuL7Ip4rt80uND4rYF0o" - - def get_content_type(file_name): - return mimetypes.guess_type(file_name)[0] or "application/octet-stream" - - def encode_field(field_name): - return ("--" + boundary, - 'Content-Disposition: form-data; name="%s"' % (field_name), - "", str(params[field_name])) - - def encode_file(field_name): - file_name, file_handle = files[field_name] - return ("--" + boundary, - 'Content-Disposition: form-data; name="%s"; filename="%s"' % (field_name, file_name), - "Content-Type: " + get_content_type(file_name), - "", file_handle.read()) - - lines = [] - for field in params: - lines.extend(encode_field(field)) - for field in files: - lines.extend(encode_file(field)) - lines.extend(("--%s--" % (boundary), "")) - body = "\r\n".join(lines) - - headers = {"Content-Type": "multipart/form-data; boundary=" + boundary, - "Content-Length": str(len(body))} - - return body, headers - - def prepare_and_make_request(self, method, path, params, include_secret=False): - url, method, body, headers = self.prepare_request(method, path, params, include_secret) - return self.make_request(url, method, body, headers) - - def prepare_request(self, method, path, params, include_secret=False): - url = body = None - headers = {} - - if not params.get('files'): - if method == "POST": - body = self._post_body(params) - headers = {'Content-type': 'application/x-www-form-urlencoded'} - url = self._full_url(path, include_secret) - else: - url = self._full_url_with_params(path, params, include_secret) - else: - body, headers = self._encode_multipart(params, params['files']) - url = self._full_url(path) - - return url, method, body, headers - - def make_request(self, url, method="GET", body=None, headers=None): - headers = headers or {} - if not 'User-Agent' in headers: - headers.update({"User-Agent": "%s Python Client" % self.api.api_name}) - http_obj = Http(disable_ssl_certificate_validation=True) - return http_obj.request(url, method, body=body, headers=headers) diff --git a/build/lib/instagram/subscriptions.py b/build/lib/instagram/subscriptions.py deleted file mode 100644 index 227b52bb..00000000 --- a/build/lib/instagram/subscriptions.py +++ /dev/null @@ -1,58 +0,0 @@ -import hmac -import hashlib -from .json_import import simplejson - -class SubscriptionType: - TAG = 'tag' - USER = 'user' - GEOGRAPHY = 'geography' - LOCATION = 'location' - - -class SubscriptionError(Exception): - pass - - -class SubscriptionVerifyError(SubscriptionError): - pass - - -class SubscriptionsReactor(object): - - callbacks = {} - - def _process_update(self, update): - object_callbacks = self.callbacks.get(update['object'], []) - - for callback in object_callbacks: - callback(update) - - def process(self, client_secret, raw_response, x_hub_signature): - if not self._verify_signature(client_secret, raw_response, x_hub_signature): - raise SubscriptionVerifyError("X-Hub-Signature and hmac digest did not match") - - try: - response = simplejson.loads(raw_response) - except ValueError: - raise SubscriptionError('Unable to parse response, not valid JSON.') - - for update in response: - self._process_update(update) - - def register_callback(self, object_type, callback): - cb_list = self.callbacks.get(object_type, []) - - if callback not in cb_list: - cb_list.append(callback) - self.callbacks[object_type] = cb_list - - def deregister_callback(self, object_type, callback): - callbacks = self.callbacks.get(object_type, []) - callbacks.remove(callback) - - def _verify_signature(self, client_secret, raw_response, x_hub_signature): - digest = hmac.new(client_secret.encode('utf-8'), - msg=raw_response.encode('utf-8'), - digestmod=hashlib.sha1 - ).hexdigest() - return digest == x_hub_signature diff --git a/dist/python_instagram-1.1.1-py3.4.egg b/dist/python_instagram-1.1.1-py3.4.egg deleted file mode 100644 index f8662a5ac60c4047c59bb06bd6352ba1494350a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25962 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