Skip to content
This repository was archived by the owner on Jan 13, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
d4fe1f5
beat > the JSON object must be str, not 'bytes'
survtur Oct 20, 2015
57a09ad
encoding
survtur Oct 20, 2015
05f18c7
Making the word an alive hyperlink
KrishMunot Nov 14, 2015
8b02858
Update user_media_feed
kmlebedev Aug 27, 2016
17f11a0
specify urlencoded Content-Type (exchange_for_...)
Jan 30, 2017
0b67a18
Safely fetch comment data, by checking availability before calling
wkoot Feb 27, 2017
93ee521
pep8 tests
wkoot Feb 27, 2017
79badce
Merge branch 'master' into safe_comments_data
wkoot Feb 27, 2017
cfe9b14
Add test case for fixed logic
wkoot Feb 27, 2017
c5d03af
Merge pull request #1 from wkoot/safe_comments_data
wkoot Feb 27, 2017
f04d178
pep8 formatting
wkoot Feb 27, 2017
f2e1c31
Safely fetch entry data, by checking availability before calling
wkoot Feb 27, 2017
e40306c
Add test case for missing field videos
wkoot Feb 27, 2017
a6d83a8
Merge pull request #2 from wkoot/safe_object_from_dictionary
wkoot Feb 27, 2017
cb65d88
Merge pull request #3 from hosseinamin/patch-1
wkoot Feb 27, 2017
bbca28d
pep8 formatting
wkoot Feb 27, 2017
d61e687
Merge pull request #4 from kmlebedev/patch-1
wkoot Feb 27, 2017
08096be
Clean up and use six directly
wkoot Feb 27, 2017
daa9854
Fix travis build
wkoot Feb 27, 2017
4ea5b65
Merge pull request #5 from KrishMunot/patch-2
wkoot Feb 27, 2017
a63b886
Merge pull request #6 from survtur/sim
wkoot Feb 27, 2017
22f062f
Cherry pick 75c32cf1db32650a05236220758df369423e8c58
kavirajk Aug 17, 2015
5bd95e6
Cherry pick a28e4b7493f47913bf2b91876cd28b5c5ffa503f and also add in …
wkoot Feb 27, 2017
537735d
Incorrect place to add pytz requirement, needs setup.py
wkoot Feb 27, 2017
f33638e
Support python 2.6 for tests
wkoot Feb 27, 2017
a8704d3
Merge pull request #7 from wkoot/kavirajk-timezone-aware-datetime-object
wkoot Feb 27, 2017
248f55a
Cherry pick 93cd4fd4fbf0c3b3d1e050fbc950089aba1c23e3
Oct 5, 2015
34e3c3f
Merge pull request #9 from wkoot/ccstolley-timeout-parameter
wkoot Feb 27, 2017
ef43834
Raise SkipTest instead of del AuthTests and make test pass on case-se…
wkoot Feb 27, 2017
d3bc7d7
Cherry pick 2d5768b6f71f4312da0bf4084a6ca1fb7eba7525
wkoot Feb 27, 2017
b283cb9
Remove now obsolete else
wkoot Feb 27, 2017
6b07bdf
Skip AuthTests in a python 2.6 friendly way
wkoot Feb 27, 2017
97af5fb
All forms of skipping are not there, don't want to import unittest2..…
wkoot Feb 27, 2017
9702c7f
Revert previous attempt; try within the class
wkoot Feb 27, 2017
4207a55
Merge pull request #11 from wkoot/p26-skip-tests
wkoot Feb 27, 2017
dc5bc2e
Merge branch 'master' into stantonk-error-signing-posts
wkoot Feb 27, 2017
853f8d3
Merge pull request #10 from wkoot/stantonk-error-signing-posts
wkoot Feb 27, 2017
db766d6
Toss and ignore old folders
wkoot Feb 27, 2017
f025c75
Update pypi package info
wkoot Feb 27, 2017
4fd4334
Set version to 1.3.3
wkoot Feb 27, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
.DS_Store
*.swp
test_settings.py

dist
*.egg-info
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,3 @@ python:
install:
- "pip install ."
script: "python tests.py"

20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Our [developer site](http://instagram.com/developer) documents all the Instagram

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).
The [Developer Blog](http://developers.instagram.com/) 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).


Community
Expand Down Expand Up @@ -225,6 +225,24 @@ except InstagramAPIError as e:
print "\nUser is set to private."
```

Setting Timeouts
------
By default there is no timeout for requests to the Instagram API. You can specify a timeout in one of two ways:
``` python
from instagram.client import InstagramAPI

# set a 30-second timeout for this particular InstagramAPI instance
api = InstagramAPI(access_token=access_token, client_secret=client_secret, timeout=30)
```
or
``` python
import socket

# Set the global default timeout, which applies to all sockets in your
# program where a timeout is not otherwise specified.
socket.setdefaulttimeout(30)
```

Trouble Shooting
------

Expand Down
Binary file removed dist/python-instagram-1.0.0.tar.gz
Binary file not shown.
Binary file removed dist/python-instagram-1.0.1.tar.gz
Binary file not shown.
Binary file removed dist/python-instagram-1.1.0.tar.gz
Binary file not shown.
Binary file removed dist/python-instagram-1.1.1.tar.gz
Binary file not shown.
Binary file removed dist/python-instagram-1.1.2.tar.gz
Binary file not shown.
108 changes: 108 additions & 0 deletions fixtures/media_search.json
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,114 @@
"profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_113603_75sq_1287035206.jpg",
"id": "113603"
}
},
{
"type": "image",
"tags": [],
"location": {
"latitude": 37.775180799999987,
"id": "68841",
"longitude": -122.2270716,
"name": "El Novillo Taco Truck"
},
"comments": {
"count": 1
},
"caption": {
"created_time": "1287585453",
"text": "Image with broken comment data ",
"from": {
"username": "darodriguez",
"first_name": "David",
"last_name": "Rodriguez",
"type": "user",
"id": "113603"
},
"id": "495311"
},
"link": "http://localhost:8000/p/C5Wr/",
"likes": {
"count": 0
},
"created_time": "1287585407",
"images": {
"low_resolution": {
"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,
"height": 150
},
"standard_resolution": {
"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": {
"username": "darodriguez",
"profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_113603_75sq_1287035206.jpg",
"id": "113603"
}
},
{
"type": "video",
"tags": [],
"location": {
"latitude": 37.775180799999987,
"id": "68841",
"longitude": -122.2270716,
"name": "El Novillo Taco Truck"
},
"comments": {
"count": 0
},
"caption": {
"created_time": "1287585453",
"text": "Type video without having videos in data",
"from": {
"username": "darodriguez",
"first_name": "David",
"last_name": "Rodriguez",
"type": "user",
"id": "113603"
},
"id": "495311"
},
"link": "http://localhost:8000/p/C5Wr/",
"likes": {
"count": 0
},
"created_time": "1287585407",
"images": {
"low_resolution": {
"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,
"height": 150
},
"standard_resolution": {
"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": {
"username": "darodriguez",
"profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_113603_75sq_1287035206.jpg",
"id": "113603"
}
}
]
}
2 changes: 2 additions & 0 deletions instagram/bind.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ 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' or response['status'] == '429':
raise InstagramAPIError(response['status'], "Rate limited", "Your client is making too many request per second")
if hasattr(content, "decode"):
content = content.decode('utf-8')
try:
content_obj = simplejson.loads(content)
except ValueError:
Expand Down
2 changes: 1 addition & 1 deletion instagram/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def __init__(self, *args, **kwargs):
root_class=Media)

user_media_feed = bind_method(
path="/users/self/feed",
path="/users/self/media/recent",
accepts_parameters=MEDIA_ACCEPT_PARAMETERS,
root_class=Media,
paginates=True)
Expand Down
4 changes: 3 additions & 1 deletion instagram/helper.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import calendar
from datetime import datetime
import pytz


def timestamp_to_datetime(ts):
return datetime.utcfromtimestamp(float(ts))
naive = datetime.utcfromtimestamp(float(ts))
return naive.replace(tzinfo=pytz.UTC)


def datetime_to_timestamp(dt):
Expand Down
61 changes: 22 additions & 39 deletions instagram/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,17 @@


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)

def __repr__(self):
return str(self)
# if six.PY2:
# return six.text_type(self).encode('utf8')
# else:
# return self.encode('utf8')

def __str__(self):
if six.PY3:
Expand All @@ -27,7 +23,6 @@ def __str__(self):


class Image(ApiModel):

def __init__(self, url, width, height):
self.url = url
self.height = height
Expand All @@ -38,13 +33,11 @@ def __unicode__(self):


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 six.iteritems(kwargs):
Expand All @@ -62,71 +55,65 @@ def get_low_resolution_url(self):
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 six.iteritems(entry['images']):
for version, version_info in six.iteritems(entry.get('images', {})):
new_media.images[version] = Image.object_from_dictionary(version_info)

if new_media.type == 'video':
new_media.videos = {}
for version, version_info in six.iteritems(entry['videos']):
for version, version_info in six.iteritems(entry.get('videos', {})):
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.user_has_liked = entry.get('user_has_liked', False)

new_media.like_count = entry.get('likes', {}).get('count', 0)
new_media.likes = []
if 'data' in entry['likes']:
for like in entry['likes']['data']:
if new_media.like_count:
for like in entry.get('likes', {}).get('data', []):
new_media.likes.append(User.object_from_dictionary(like))

new_media.comment_count = entry['comments']['count']
new_media.comment_count = entry.get('comments', {}).get('count', 0)
new_media.comments = []
for comment in entry['comments']['data']:
new_media.comments.append(Comment.object_from_dictionary(comment))
if new_media.comment_count:
for comment in entry.get('comments', {}).get('data', []):
new_media.comments.append(Comment.object_from_dictionary(comment))

new_media.users_in_photo = []
if entry.get('users_in_photo'):
for user_in_photo in entry['users_in_photo']:
new_media.users_in_photo.append(UserInPhoto.object_from_dictionary(user_in_photo))
for user_in_photo in entry.get('users_in_photo') or []:
new_media.users_in_photo.append(UserInPhoto.object_from_dictionary(user_in_photo))

new_media.created_time = timestamp_to_datetime(entry['created_time'])

if entry['location'] and 'id' in entry:
if entry.get('location') and entry.get('id'):
new_media.location = Location.object_from_dictionary(entry['location'])

new_media.caption = None
if entry['caption']:
if entry.get('caption'):
new_media.caption = Comment.object_from_dictionary(entry['caption'])

new_media.tags = []
if entry['tags']:
for tag in entry['tags']:
new_media.tags.append(Tag.object_from_dictionary({'name': tag}))
for tag in entry.get('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 six.iteritems(kwargs):
Expand Down Expand Up @@ -179,19 +166,16 @@ def __init__(self, id, *args, **kwargs):
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', ''))
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 six.iteritems(kwargs):
Expand All @@ -202,7 +186,6 @@ def __unicode__(self):


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
Expand Down
Loading