77import hmac
88import urllib
99import uuid
10+ import time
11+ from ImageUtils import getImageSize
1012
1113class InstagramAPI :
1214 API_URL = 'https://i.instagram.com/api/v1/'
13- USER_AGENT = 'Instagram 9.2.0 Android (18/4.3; 320dpi; 720x1280; Xiaomi; HM 1SW; armani; qcom; en_US)'
15+ DEVICE_SETTINTS = {
16+ 'manufacturer' : 'Xiaomi' ,
17+ 'model' : 'HM 1SW' ,
18+ 'android_version' : 18 ,
19+ 'android_release' : '4.3'
20+ }
21+ USER_AGENT = 'Instagram 9.2.0 Android ({android_version}/{android_release}; 320dpi; 720x1280; {manufacturer}; {model}; armani; qcom; en_US)' .format (** DEVICE_SETTINTS )
1422 IG_SIG_KEY = '012a54f51c49aa8c5c322416ab1410909add32c966bbaa0fe3dc58ac43fd7ede'
1523 EXPERIMENTS = 'ig_android_progressive_jpeg,ig_creation_growth_holdout,ig_android_report_and_hide,ig_android_new_browser,ig_android_enable_share_to_whatsapp,ig_android_direct_drawing_in_quick_cam_universe,ig_android_huawei_app_badging,ig_android_universe_video_production,ig_android_asus_app_badging,ig_android_direct_plus_button,ig_android_ads_heatmap_overlay_universe,ig_android_http_stack_experiment_2016,ig_android_infinite_scrolling,ig_fbns_blocked,ig_android_white_out_universe,ig_android_full_people_card_in_user_list,ig_android_post_auto_retry_v7_21,ig_fbns_push,ig_android_feed_pill,ig_android_profile_link_iab,ig_explore_v3_us_holdout,ig_android_histogram_reporter,ig_android_anrwatchdog,ig_android_search_client_matching,ig_android_high_res_upload_2,ig_android_new_browser_pre_kitkat,ig_android_2fac,ig_android_grid_video_icon,ig_android_white_camera_universe,ig_android_disable_chroma_subsampling,ig_android_share_spinner,ig_android_explore_people_feed_icon,ig_explore_v3_android_universe,ig_android_media_favorites,ig_android_nux_holdout,ig_android_search_null_state,ig_android_react_native_notification_setting,ig_android_ads_indicator_change_universe,ig_android_video_loading_behavior,ig_android_black_camera_tab,liger_instagram_android_univ,ig_explore_v3_internal,ig_android_direct_emoji_picker,ig_android_prefetch_explore_delay_time,ig_android_business_insights_qe,ig_android_direct_media_size,ig_android_enable_client_share,ig_android_promoted_posts,ig_android_app_badging_holdout,ig_android_ads_cta_universe,ig_android_mini_inbox_2,ig_android_feed_reshare_button_nux,ig_android_boomerang_feed_attribution,ig_android_fbinvite_qe,ig_fbns_shared,ig_android_direct_full_width_media,ig_android_hscroll_profile_chaining,ig_android_feed_unit_footer,ig_android_media_tighten_space,ig_android_private_follow_request,ig_android_inline_gallery_backoff_hours_universe,ig_android_direct_thread_ui_rewrite,ig_android_rendering_controls,ig_android_ads_full_width_cta_universe,ig_video_max_duration_qe_preuniverse,ig_android_prefetch_explore_expire_time,ig_timestamp_public_test,ig_android_profile,ig_android_dv2_consistent_http_realtime_response,ig_android_enable_share_to_messenger,ig_explore_v3,ig_ranking_following,ig_android_pending_request_search_bar,ig_android_feed_ufi_redesign,ig_android_video_pause_logging_fix,ig_android_default_folder_to_camera,ig_android_video_stitching_7_23,ig_android_profanity_filter,ig_android_business_profile_qe,ig_android_search,ig_android_boomerang_entry,ig_android_inline_gallery_universe,ig_android_ads_overlay_design_universe,ig_android_options_app_invite,ig_android_view_count_decouple_likes_universe,ig_android_periodic_analytics_upload_v2,ig_android_feed_unit_hscroll_auto_advance,ig_peek_profile_photo_universe,ig_android_ads_holdout_universe,ig_android_prefetch_explore,ig_android_direct_bubble_icon,ig_video_use_sve_universe,ig_android_inline_gallery_no_backoff_on_launch_universe,ig_android_image_cache_multi_queue,ig_android_camera_nux,ig_android_immersive_viewer,ig_android_dense_feed_unit_cards,ig_android_sqlite_dev,ig_android_exoplayer,ig_android_add_to_last_post,ig_android_direct_public_threads,ig_android_prefetch_venue_in_composer,ig_android_bigger_share_button,ig_android_dv2_realtime_private_share,ig_android_non_square_first,ig_android_video_interleaved_v2,ig_android_follow_search_bar,ig_android_last_edits,ig_android_video_download_logging,ig_android_ads_loop_count_universe,ig_android_swipeable_filters_blacklist,ig_android_boomerang_layout_white_out_universe,ig_android_ads_carousel_multi_row_universe,ig_android_mentions_invite_v2,ig_android_direct_mention_qe,ig_android_following_follower_social_context'
1624 SIG_KEY_VERSION = '4'
@@ -105,7 +113,29 @@ def logout(self):
105113 # TODO Instagram.php 180-185
106114
107115 def uploadPhoto (self , photo , caption = None , upload_id = None ):
108- # TODO Instagram.php 200-290
116+ if upload_id is None :
117+ upload_id = str (int (time .time () * 1000 ))
118+ from requests_toolbelt import MultipartEncoder
119+ data = {
120+ 'upload_id' : upload_id ,
121+ '_uuid' : self .uuid ,
122+ '_csrftoken' : self .token ,
123+ 'image_compression' : '{"lib_name":"jt","lib_version":"1.3.0","quality":"87"}' ,
124+ 'photo' : ('pending_media_%s.jpg' % upload_id , open (photo , 'rb' ), 'application/octet-stream' , {'Content-Transfer-Encoding' :'binary' })
125+ }
126+ m = MultipartEncoder (data , boundary = self .uuid )
127+ self .s .headers .update ({'X-IG-Capabilities' : '3Q4=' ,
128+ 'X-IG-Connection-Type' : 'WIFI' ,
129+ 'Cookie2' : '$Version=1' ,
130+ 'Accept-Language' : 'en-US' ,
131+ 'Accept-Encoding' : 'gzip, deflate' ,
132+ 'Content-type' : m .content_type ,
133+ 'Connection' : 'close' ,
134+ 'User-Agent' : self .USER_AGENT })
135+ response = self .s .post (self .API_URL + "upload/photo/" , data = m .to_string ())
136+ if response .status_code == 200 :
137+ if self .configure (upload_id , photo , caption ):
138+ self .expose ()
109139 return False
110140
111141 def uploadVideo (self , video , caption = None ):
@@ -116,13 +146,31 @@ def direct_share(self, media_id, recipients, text = None):
116146 # TODO Instagram.php 420-490
117147 return False
118148
119- def configureVideo (upload_id , video , caption = '' ):
149+ def configureVideo (self , upload_id , video , caption = '' ):
120150 # TODO Instagram.php 490-530
121151 return False
122152
123- def configure (upload_id , photo , caption = '' ):
124- # TODO Instagram.php 530-570
125- return False
153+ def configure (self , upload_id , photo , caption = '' ):
154+ (w ,h ) = getImageSize (photo )
155+ data = json .dumps ({
156+ '_csrftoken' : self .token ,
157+ 'media_folder' : 'Instagram' ,
158+ 'source_type' : 4 ,
159+ '_uid' : self .username_id ,
160+ '_uuid' : self .uuid ,
161+ 'caption' : caption ,
162+ 'upload_id' : upload_id ,
163+ 'device' : self .DEVICE_SETTINTS ,
164+ 'edits' : {
165+ 'crop_original_size' : [w * 1.0 , h * 1.0 ],
166+ 'crop_center' : [0.0 , 0.0 ],
167+ 'crop_zoom' : 1.0
168+ },
169+ 'extra' : {
170+ 'source_width' : w ,
171+ 'source_height' : h ,
172+ }})
173+ return self .SendRequest ('media/configure/?' , self .generateSignature (data ))
126174
127175 def editMedia (self , mediaId , captionText = '' ):
128176 data = json .dumps ({
@@ -430,7 +478,7 @@ def getLikedMedia(self,maxid=''):
430478 return self .SendRequest ('feed/liked/?max_id=' + str (maxid ))
431479
432480 def generateSignature (self , data ):
433- return 'ig_sig_key_version=' + self .SIG_KEY_VERSION + '&signed_body=' + hmac .new (self .IG_SIG_KEY .encode ('utf-8' ), data .encode ('utf-8' ), hashlib .sha256 ).hexdigest () + '.' + urllib .parse . quote (data )
481+ return 'ig_sig_key_version=' + self .SIG_KEY_VERSION + '&signed_body=' + hmac .new (self .IG_SIG_KEY .encode ('utf-8' ), data .encode ('utf-8' ), hashlib .sha256 ).hexdigest () + '.' + urllib .quote (data )
434482
435483 def generateDeviceId (self , seed ):
436484 volatile_seed = "12345"
0 commit comments