diff --git a/EXAMPLES.md b/EXAMPLES.md index 2ec2cb7..fdcb57a 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -62,15 +62,15 @@ For endpoints that require usernames, once a user is authenticated we can use th ```python for album in client.get_account_albums('me'): -album_title = album.title if album.title else 'Untitled' -print('Album: {0} ({1})'.format(album_title, album.id)) + album_title = album.title if album.title else 'Untitled' + print('Album: {0} ({1})'.format(album_title, album.id)) -for image in client.get_album_images(album.id): - image_title = image.title if image.title else 'Untitled' - print('\t{0}: {1}'.format(image_title, image.link)) + for image in client.get_album_images(album.id): + image_title = image.title if image.title else 'Untitled' + print('\t{0}: {1}'.format(image_title, image.link)) -# Save some API credits by not getting all albums -break + # Save some API credits by not getting all albums + break ``` ***Output*** diff --git a/README.md b/README.md index c89b846..0dc5ef8 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -# The imgurpython project is no longer supported. - imgurpython =========== -A Python client for the [Imgur API](http://api.imgur.com/). It can be used to +Note: This fork picks up where the original left off. After the authors abandoned their original, a number of PRs came in and I'll merge them here. + +A Python client for the [Imgur API](https://api.imgur.com/). It can be used to interact with the Imgur API in your projects. -You must [register](http://api.imgur.com/oauth2/addclient) your client with the Imgur API, and provide the Client-ID to +You must [register](https://api.imgur.com/oauth2/addclient) your client with the Imgur API, and provide the Client-ID to make *any* request to the API (see the [Authentication](https://api.imgur.com/#authentication) note). If you want to perform actions on accounts, the user will have to authorize your application through OAuth2. @@ -14,17 +14,12 @@ Requirements ------------ - Python >= 2.7 -- [requests](http://docs.python-requests.org/en/latest/user/install/) +- [requests][req] Imgur API Documentation ----------------------- -Our developer documentation can be found [here](https://api.imgur.com/). - -Community ---------- - -The best way to reach out to Imgur for API support is emailing us at api@imgur.com. +See the [developer documentation][devdocs]. Installation ------------ @@ -95,9 +90,9 @@ Error types ```python from imgurpython.helpers.error import ImgurClientError -try +try: ... -except ImgurClientError as e +except ImgurClientError as e: print(e.error_message) print(e.status_code) ``` @@ -114,11 +109,11 @@ To view client and user credit information, use the `credits` attribute of `Imgu * ClientLimit * ClientRemaining -For more information about rate-limiting, please see the note in our [docs](http://api.imgur.com/#limits)! +For more information about rate-limiting, please see the note in our [docs][devdocs1]. Examples ------------ -Examples can be found [here](EXAMPLES.md) +Examples are [available](EXAMPLES.md) ## ImgurClient Functions @@ -234,3 +229,6 @@ Imgur entry points | imgur.com/a/{album_id}#{image_id} | single image from an album | | imgur.com/gallery/{gallery_post_id} | gallery | +[devdocs]:https://api.imgur.com/ +[devdocs1]:https://api.imgur.com/#limits +[req]:https://docs.python-requests.org/en/latest/user/install/ diff --git a/imgurpython/client.py b/imgurpython/client.py index 9c41ea6..8318412 100644 --- a/imgurpython/client.py +++ b/imgurpython/client.py @@ -1,6 +1,7 @@ import base64 import requests from .imgur.models.tag import Tag +from .imgur.models.topic import Topic from .imgur.models.album import Album from .imgur.models.image import Image from .imgur.models.account import Account @@ -140,13 +141,14 @@ def make_request(self, method, route, data=None, force_anon=False): else: response = method_to_call(url, headers=header, data=data) - self.credits = { - 'UserLimit': response.headers.get('X-RateLimit-UserLimit'), - 'UserRemaining': response.headers.get('X-RateLimit-UserRemaining'), - 'UserReset': response.headers.get('X-RateLimit-UserReset'), - 'ClientLimit': response.headers.get('X-RateLimit-ClientLimit'), - 'ClientRemaining': response.headers.get('X-RateLimit-ClientRemaining') - } + if response.headers.get('X-RateLimit-UserLimit') is not None: + self.credits = { + 'UserLimit': int(response.headers.get('X-RateLimit-UserLimit')), + 'UserRemaining': int(response.headers.get('X-RateLimit-UserRemaining')), + 'UserReset': int(response.headers.get('X-RateLimit-UserReset')), + 'ClientLimit': int(response.headers.get('X-RateLimit-ClientLimit')), + 'ClientRemaining': int(response.headers.get('X-RateLimit-ClientRemaining')) + } # Rate-limit check if response.status_code == 429: @@ -184,6 +186,18 @@ def get_account(self, username): account_data['pro_expiration'], ) + def get_account_by_id(self, userid): + account_data = self.make_request('GET', 'account/?account_id=%i' % userid) + + return Account( + account_data['id'], + account_data['url'], + account_data['bio'], + account_data['reputation'], + account_data['created'], + account_data['pro_expiration'], + ) + def get_gallery_favorites(self, username, page=0): self.validate_user_context(username) gallery_favorites = self.make_request('GET', 'account/%s/gallery_favorites/%d' % (username, page)) @@ -283,13 +297,17 @@ def get_album_images(self, album_id): images = self.make_request('GET', 'album/%s/images' % album_id) return [Image(image) for image in images] + def get_album_image(self, album_id, image_id): + image = self.make_request('GET', 'album/%s/image/%s' % (album_id, image_id)) + return Image(image) + def create_album(self, fields): post_data = {field: fields[field] for field in set(self.allowed_album_fields).intersection(fields.keys())} - if 'ids' in post_data: - self.logged_in() - - return self.make_request('POST', 'album', data=post_data) + if self.auth is None: + return self.make_request('POST', 'album', post_data, True) + else: + return self.make_request('POST', 'album', post_data) def update_album(self, album_id, fields): post_data = {field: fields[field] for field in set(self.allowed_album_fields).intersection(fields.keys())} @@ -552,9 +570,9 @@ def gallery_item(self, item_id): response = self.make_request('GET', 'gallery/%s' % item_id) return build_gallery_images_and_albums(response) - def report_gallery_item(self, item_id): + def report_gallery_item(self, item_id, reason): self.logged_in() - return self.make_request('POST', 'gallery/%s/report' % item_id) + return self.make_request('POST', 'gallery/%s/report' % item_id, {'reason': reason}) def gallery_item_vote(self, item_id, vote='up'): self.logged_in() @@ -581,21 +599,21 @@ def get_image(self, image_id): def upload_from_path(self, path, config=None, anon=True): with open(path, 'rb') as fd: - self.upload(fd, config, anon) + contents = fd.read() + b64 = base64.b64encode(contents) + return self.upload(b64, config, anon) def upload(self, fd, config=None, anon=True): if not config: config = dict() - contents = fd.read() - b64 = base64.b64encode(contents) data = { - 'image': b64, + 'image': fd, 'type': 'base64', } data.update({meta: config[meta] for meta in set(self.allowed_image_fields).intersection(config.keys())}) - - return self.make_request('POST', 'upload', data, anon) + + return self.make_request('POST', 'image', data, anon) def upload_from_url(self, url, config=None, anon=True): if not config: @@ -612,6 +630,13 @@ def upload_from_url(self, url, config=None, anon=True): def delete_image(self, image_id): return self.make_request('DELETE', 'image/%s' % image_id) + def update_image(self, image_id, fields): + ''' + Note: Can only update title or description of image + ''' + post_data = {field: fields[field] for field in set({'title', 'description'}).intersection(fields.keys())} + return self.make_request('POST', 'image/%s' % image_id, data=post_data) + def favorite_image(self, image_id): self.logged_in() return self.make_request('POST', 'image/%s/favorite' % image_id) @@ -681,3 +706,20 @@ def mark_notifications_as_read(self, notification_ids): def default_memes(self): response = self.make_request('GET', 'memegen/defaults') return [Image(meme) for meme in response] + + # Topic-related endpoints + def default_topics(self): + topics = self.make_request('GET', 'topics/defaults') + return [Topic(topic) for topic in topics] + + def gallery_topic(self, topic_id, sort='viral', page=0, windown='week'): + if sort == 'top': + response = self.make_request('GET', 'topics/%s/%s/%s/%d' % (topic_id, sort, window, page)) + else: + response = self.make_request('GET', 'topics/%s/%s/%d' % (topic_id, sort, page)) + + return build_gallery_images_and_albums(response) + + def gallery_topic_item(self, topic_id, item_id): + response = self.make_request('GET', 'topics/%s/%s' % (topic_id, item_id)) + return build_gallery_images_and_albums(response) diff --git a/imgurpython/imgur/models/topic.py b/imgurpython/imgur/models/topic.py new file mode 100644 index 0000000..2d0f336 --- /dev/null +++ b/imgurpython/imgur/models/topic.py @@ -0,0 +1,8 @@ +class Topic(object): + + def __init__(self, *initial_data, **kwargs): + for dictionary in initial_data: + for key, value in dictionary.iteritems(): + setattr(self, key, value) + for key, value in kwargs.iteritems(): + setattr(self, key, value) diff --git a/setup.py b/setup.py index 300e786..05e44ae 100644 --- a/setup.py +++ b/setup.py @@ -11,17 +11,17 @@ # Versions should comply with PEP440. For a discussion on single-sourcing # the version across setup.py and the project code, see # http://packaging.python.org/en/latest/tutorial.html#version - version='1.1.7', + version='1.1.9', - description='Official Imgur python library with OAuth2 and samples', - long_description='', + description='Imgur python library with OAuth2 and samples', + long_description='Forked and patched version, now maintained!', # The project's main homepage. url='https://github.com/Imgur/imgurpython', # Author details - author='Imgur Inc.', - author_email='api@imgur.com', + author='BryanH', + author_email='BryanH@example.com', # Choose your license license='MIT', @@ -47,6 +47,7 @@ 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.6', ], # What does your project relate to?