1+ import requests
2+ from imgur .models .album import Album
3+ from imgur .models .image import Image
4+ from imgur .models .account import Account
5+ from imgur .models .comment import Comment
6+ from helpers .error import ImgurClientError
7+ from helpers .format import build_comment_tree
8+ from imgur .models .gallery_album import GalleryAlbum
9+ from imgur .models .gallery_image import GalleryImage
10+ from imgur .models .account_settings import AccountSettings
11+
12+ API_URL = 'https://api.imgur.com/'
13+
14+
15+ class AuthWrapper :
16+ def __init__ (self , access_token , refresh_token , client_id , client_secret ):
17+ self .current_access_token = access_token
18+
19+ if refresh_token is None :
20+ raise TypeError ('A refresh token must be provided' )
21+
22+ self .refresh_token = refresh_token
23+ self .client_id = client_id
24+ self .client_secret = client_secret
25+
26+ def get_refresh_token (self ):
27+ return self .refresh_token
28+
29+ def get_current_access_token (self ):
30+ return self .current_access_token
31+
32+ def refresh (self ):
33+ data = {
34+ 'refresh_token' : self .refresh_token ,
35+ 'client_id' : self .client_id ,
36+ 'client_secret' : self .client_secret ,
37+ 'grant_type' : 'refresh_token'
38+ }
39+
40+ url = API_URL + 'oauth2/token'
41+
42+ response = requests .post (url , data = data )
43+
44+ if response .status_code != 200 :
45+ raise ImgurClientError ('Error refreshing access token!' , response .status_code )
46+
47+ response_data = response .json ()
48+ self .current_access_token = response_data ['access_token' ]
49+
50+
51+ class ImgurClient :
52+ allowed_album_fields = {
53+ 'ids' , 'title' , 'description' , 'privacy' , 'layout' , 'cover'
54+ }
55+
56+ def __init__ (self , client_id = None , client_secret = None , access_token = None , refresh_token = None ):
57+ self .client_id = client_id
58+ self .client_secret = client_secret
59+ self .auth = None
60+
61+ if refresh_token is not None :
62+ self .auth = AuthWrapper (access_token , refresh_token , client_id , client_secret )
63+
64+ def get_client_id (self ):
65+ return self .client_id
66+
67+ def prepare_headers (self ):
68+ if self .auth is None :
69+ if self .client_id is None :
70+ raise ImgurClientError ('Client credentials not found!' )
71+ else :
72+ return {'Authorization' : 'Client-ID %s' % self .get_client_id ()}
73+ else :
74+ return {'Authorization' : 'Bearer %s' % self .auth .get_current_access_token ()}
75+
76+ def make_request (self , method , route , data = None ):
77+ method = method .lower ()
78+ method_to_call = getattr (requests , method )
79+
80+ header = self .prepare_headers ()
81+ url = API_URL + '3/%s' % route
82+
83+ if method == 'delete' :
84+ response = method_to_call (url , headers = header , params = data )
85+ else :
86+ response = method_to_call (url , headers = header , data = data )
87+
88+ if response .status_code == 403 and self .auth is not None :
89+ self .auth .refresh ()
90+ header = self .prepare_headers ()
91+ if method == 'delete' :
92+ response = method_to_call (url , headers = header , params = data )
93+ else :
94+ response = method_to_call (url , headers = header , data = data )
95+
96+ # Rate-limit check
97+ if response .status_code == 429 :
98+ raise ImgurClientError ('Rate-limit exceeded!' )
99+
100+ try :
101+ response_data = response .json ()
102+ except :
103+ raise ImgurClientError ('JSON decoding of response failed.' )
104+
105+ if isinstance (response_data ['data' ], dict ) and 'error' in response_data ['data' ]:
106+ raise ImgurClientError (response_data ['data' ]['error' ], response .status_code )
107+
108+ return response_data ['data' ]
109+
110+ def validate_user_context (self , username ):
111+ if username == 'me' and self .auth is None :
112+ raise ImgurClientError ('\' me\' can only be used in the authenticated context.' )
113+
114+ def logged_in (self ):
115+ if self .auth is None :
116+ raise ImgurClientError ('Must be logged in to complete request.' )
117+
118+ @staticmethod
119+ def build_gallery_images_and_albums (items ):
120+ result = []
121+ for item in items :
122+ if item ['is_album' ]:
123+ result .append (GalleryAlbum (item ))
124+ else :
125+ result .append (GalleryImage (item ))
126+
127+ return result
128+
129+ # Account-related endpoints
130+ def get_account (self , username ):
131+ self .validate_user_context (username )
132+ account_data = self .make_request ('GET' , 'account/%s' % username )
133+
134+ return Account (
135+ account_data ['id' ],
136+ account_data ['url' ],
137+ account_data ['bio' ],
138+ account_data ['reputation' ],
139+ account_data ['created' ],
140+ account_data ['pro_expiration' ],
141+ )
142+
143+ def get_gallery_favorites (self , username ):
144+ self .validate_user_context (username )
145+ gallery_favorites = self .make_request ('GET' , 'account/%s/gallery_favorites' % username )
146+
147+ return self .build_gallery_images_and_albums (gallery_favorites )
148+
149+ def get_account_favorites (self , username ):
150+ self .validate_user_context (username )
151+ favorites = self .make_request ('GET' , 'account/%s/favorites' % username )
152+
153+ return self .build_gallery_images_and_albums (favorites )
154+
155+ def get_account_submissions (self , username , page = 0 ):
156+ self .validate_user_context (username )
157+ submissions = self .make_request ('GET' , 'account/%s/submissions/%d' % (username , page ))
158+
159+ return self .build_gallery_images_and_albums (submissions )
160+
161+ def get_account_settings (self , username ):
162+ self .logged_in ()
163+ settings = self .make_request ('GET' , 'account/%s/settings' % username )
164+
165+ return AccountSettings (
166+ settings ['email' ],
167+ settings ['high_quality' ],
168+ settings ['public_images' ],
169+ settings ['album_privacy' ],
170+ settings ['pro_expiration' ],
171+ settings ['accepted_gallery_terms' ],
172+ settings ['active_emails' ],
173+ settings ['messaging_enabled' ],
174+ settings ['blocked_users' ]
175+ )
176+
177+ def change_account_settings (self , username , fields ):
178+ allowed_fields = {
179+ 'bio' , 'public_images' , 'messaging_enabled' , 'album_privacy' , 'accepted_gallery_terms' , 'username'
180+ }
181+
182+ post_data = {setting : fields [setting ] for setting in set (allowed_fields ).intersection (fields .keys ())}
183+
184+ return self .make_request ('POST' , 'account/%s/settings' % username , post_data )
185+
186+ def get_email_verification_status (self , username ):
187+ self .logged_in ()
188+ self .validate_user_context (username )
189+ return self .make_request ('GET' , 'account/%s/verifyemail' % username )
190+
191+ def send_verification_email (self , username ):
192+ self .logged_in ()
193+ self .validate_user_context (username )
194+ return self .make_request ('POST' , 'account/%s/verifyemail' % username )
195+
196+ def get_account_albums (self , username , page = 0 ):
197+ self .validate_user_context (username )
198+
199+ albums = self .make_request ('GET' , 'account/%s/albums/%d' % (username , page ))
200+ return [Album (album ) for album in albums ]
201+
202+ def get_account_album_ids (self , username , page = 0 ):
203+ self .validate_user_context (username )
204+ return self .make_request ('GET' , 'account/%s/albums/ids/%d' % (username , page ))
205+
206+ def get_account_album_count (self , username ):
207+ self .validate_user_context (username )
208+ return self .make_request ('GET' , 'account/%s/albums/count' % username )
209+
210+ def get_account_comments (self , username , sort = 'newest' , page = 0 ):
211+ self .validate_user_context (username )
212+ comments = self .make_request ('GET' , 'account/%s/comments/%s/%s' % (username , sort , page ))
213+
214+ return [Comment (comment ) for comment in comments ]
215+
216+ def get_account_comment_ids (self , username , sort = 'newest' , page = 0 ):
217+ self .validate_user_context (username )
218+ return self .make_request ('GET' , 'account/%s/comments/ids/%s/%s' % (username , sort , page ))
219+
220+ def get_account_comment_count (self , username ):
221+ self .validate_user_context (username )
222+ return self .make_request ('GET' , 'account/%s/comments/count' % username )
223+
224+ def get_account_images (self , username , page = 0 ):
225+ self .validate_user_context (username )
226+ images = self .make_request ('GET' , 'account/%s/images/%d' % (username , page ))
227+
228+ return [Image (image ) for image in images ]
229+
230+ def get_account_image_ids (self , username , page = 0 ):
231+ self .validate_user_context (username )
232+ return self .make_request ('GET' , 'account/%s/images/ids/%d' % (username , page ))
233+
234+ def get_account_images_count (self , username , page = 0 ):
235+ self .validate_user_context (username )
236+ return self .make_request ('GET' , 'account/%s/images/ids/%d' % (username , page ))
237+
238+ # Album-related endpoints
239+ def get_album (self , album_id ):
240+ album = self .make_request ('GET' , 'album/%s' % album_id )
241+ return Album (album )
242+
243+ def get_album_images (self , album_id ):
244+ images = self .make_request ('GET' , 'album/%s/images' % album_id )
245+ return [Image (image ) for image in images ]
246+
247+ def create_album (self , fields ):
248+ post_data = {field : fields [field ] for field in set (self .allowed_album_fields ).intersection (fields .keys ())}
249+
250+ if 'ids' in post_data :
251+ self .logged_in ()
252+
253+ return self .make_request ('POST' , 'album' , data = post_data )
254+
255+ def update_album (self , album_id , fields ):
256+ post_data = {field : fields [field ] for field in set (self .allowed_album_fields ).intersection (fields .keys ())}
257+
258+ if isinstance (post_data ['ids' ], list ):
259+ post_data ['ids' ] = ',' .join (post_data ['ids' ])
260+
261+ return self .make_request ('POST' , 'album/%s' % album_id , data = post_data )
262+
263+ def album_delete (self , album_id ):
264+ return self .make_request ('DELETE' , 'album/%s' % album_id )
265+
266+ def album_favorite (self , album_id ):
267+ self .logged_in ()
268+ return self .make_request ('POST' , 'album/%s/favorite' % album_id )
269+
270+ def album_set_images (self , album_id , ids ):
271+ if isinstance (ids , list ):
272+ ids = ',' .join (ids )
273+
274+ return self .make_request ('POST' , 'album/%s/' % album_id , {'ids' : ids })
275+
276+ def album_add_images (self , album_id , ids ):
277+ if isinstance (ids , list ):
278+ ids = ',' .join (ids )
279+
280+ return self .make_request ('POST' , 'album/%s/add' % album_id , {'ids' : ids })
281+
282+ def album_remove_images (self , album_id , ids ):
283+ if isinstance (ids , list ):
284+ ids = ',' .join (ids )
285+
286+ return self .make_request ('DELETE' , 'album/%s/remove_images' % album_id , {'ids' : ids })
287+
288+ # Comment-related endpoints
289+ def get_comment (self , comment_id ):
290+ comment = self .make_request ('GET' , 'comment/%d' % comment_id )
291+ return Comment (comment )
292+
293+ def delete_comment (self , comment_id ):
294+ self .logged_in ()
295+ return self .make_request ('DELETE' , 'comment/%d' % comment_id )
296+
297+ def get_comment_replies (self , comment_id ):
298+ replies = self .make_request ('GET' , 'comment/%d/replies' % comment_id )
299+ replies ['children' ] = build_comment_tree (replies ['children' ])
300+
301+ return replies
302+
303+ def post_comment_reply (self , comment_id , image_id , comment ):
304+ self .logged_in ()
305+ data = {
306+ 'image_id' : image_id ,
307+ 'comment' : comment
308+ }
309+
310+ return self .make_request ('POST' , 'comment/%d' % comment_id , data )
311+
312+ def comment_vote (self , comment_id , vote , toggle = True ):
313+ self .logged_in ()
314+ toggle_behavior = 1 if toggle else 0
315+
316+ return self .make_request ('POST' , 'comment/%d/vote/%s?toggle=%d' % (comment_id , vote , toggle_behavior ))
317+
318+ def comment_report (self , comment_id ):
319+ self .logged_in ()
320+ return self .make_request ('POST' , 'comment/%d/report' % comment_id )
0 commit comments