Source code for instagram_web_api.compatpatch

# -*- coding: utf-8 -*-
import re


[docs]class ClientCompatPatch(object): """Utility to make entities from the private api similar to the ones from the public one by adding the necessary properties, and if required, remove any incompatible properties (to save storage space for example). """ IG_IMAGE_URL_EXPR = r'/((?P<crop>[a-z])[0-9]{3}x[0-9]{3}/)' @classmethod def _generate_image_url(cls, url, size, crop): """ Try to generate an IG cropped image url. :param url: target url :param size: width/height of the image :param crop: 'p' or 's' :return: """ mobj = re.search(cls.IG_IMAGE_URL_EXPR, url) if not mobj: replacement_expr = r'\g<eparam>{crop!s}{size!s}x{size!s}/'.format( **{'crop': crop, 'size': size}) return re.sub(r'(?P<eparam>/e[0-9]+/)', replacement_expr, url) replacement_expr = '/{crop!s}{size!s}x{size!s}/'.format( **{'crop': mobj.group('crop') or crop, 'size': size}) return re.sub(cls.IG_IMAGE_URL_EXPR, replacement_expr, url) @staticmethod def _drop_keys(obj, keys): """ Remove the specified keys from the object. :param obj: target object :param keys: list of keys :return: """ if not obj: return obj for k in keys: obj.pop(k, None)
[docs] @classmethod def media(cls, media, drop_incompat_keys=False): """Patch a media object""" media_shortcode = media.get('code') or media.get('shortcode') # for media_info2 media['link'] = 'https://www.instagram.com/p/{0!s}/'.format(media_shortcode) try: caption = (media.get('caption') or media.get('edge_media_to_caption', {}).get('edges', [{}])[0].get( 'node', {}).get('text')) except IndexError: # no caption - edge_media_to_caption: { edges: [] } caption = None if not caption: media['caption'] = None else: media['caption'] = { 'text': caption, 'from': media['owner'], # generate a psuedo 12-char ID 'id': str(abs(hash(caption + media_shortcode)) % (10 ** 12)), } media['tags'] = [] media['filter'] = '' media['attribution'] = None media['user_has_liked'] = False media_user = { 'id': media['owner']['id'], } if 'username' in media['owner']: media_user['username'] = media['owner']['username'] if 'full_name' in media['owner']: media_user['full_name'] = media['owner']['full_name'] if 'profile_pic_url' in media['owner']: media_user['profile_picture'] = media['owner']['profile_pic_url'] media['user'] = media_user media['type'] = 'video' if media['is_video'] else 'image' display_src = media.get('display_src') or media.get('display_url') # for media_info2 images = { 'standard_resolution': { 'url': display_src, 'width': media['dimensions']['width'], 'height': media['dimensions']['height']}, 'low_resolution': {'url': cls._generate_image_url(display_src, '320', 'p')}, 'thumbnail': {'url': cls._generate_image_url(display_src, '150', 's')}, } media['images'] = images if media['is_video'] and media.get('video_url'): videos = { 'standard_resolution': { 'url': media['video_url'], 'width': media['dimensions']['width'], 'height': media['dimensions']['height']}, 'low_resolution': {'url': media['video_url']}, 'low_bandwidth': {'url': media['video_url']}, } media['videos'] = videos media['likes'] = { 'count': (media.get('likes', {}) or media.get('edge_liked_by', {}) or media.get('edge_media_preview_like', {})).get('count', 0), 'data': [] } media['comments'] = { 'count': (media.get('comments', {}) or media.get('edge_media_to_comment', {})).get('count', 0), 'data': [] } # Try to preserve location even if there's no lat/lng if 'location' not in media or not media['location']: media['location'] = None elif media.get('location', {}).get('lat') and media.get('location', {}).get('lng'): media['location']['latitude'] = media['location']['lat'] media['location']['longitude'] = media['location']['lng'] media['id'] = '{0!s}_{1!s}'.format(media['id'], media['owner']['id']) media['created_time'] = str( media.get('date', '') or media.get('taken_at_timestamp', '')) usertags = ( media.get('usertags', {}).get('nodes', []) or [ut['node'] for ut in media.get('edge_media_to_tagged_user', {}).get('edges', [])]) if not usertags: media['users_in_photo'] = [] else: users_in_photo = [{ 'position': {'y': ut['y'], 'x': ut['x']}, 'user': ut['user'] } for ut in usertags] media['users_in_photo'] = users_in_photo # Try to make carousel_media for app api compat if media.get('edge_sidecar_to_children', {}).get('edges', []): carousel_media = [] edges = media.get('edge_sidecar_to_children', {}).get('edges', []) for edge in edges: node = edge.get('node', {}) images = { 'standard_resolution': { 'url': node['display_url'], 'width': node['dimensions']['width'], 'height': node['dimensions']['height']}, 'low_resolution': { 'url': cls._generate_image_url(node['display_url'], '320', 'p')}, 'thumbnail': { 'url': cls._generate_image_url(node['display_url'], '150', 's')}, } node['images'] = images node['type'] = 'image' if node.get('is_video'): videos = { 'standard_resolution': { 'url': node['video_url'], 'width': node['dimensions']['width'], 'height': node['dimensions']['height']}, 'low_resolution': {'url': node['video_url']}, 'low_bandwidth': {'url': node['video_url']}, } node['videos'] = videos node['type'] = 'video' node['pk'] = node['id'] node['id'] = '{0!s}_{1!s}'.format(node['id'], media['owner']['id']) node['original_width'] = node['dimensions']['width'] node['original_height'] = node['dimensions']['height'] carousel_media.append(node) media['carousel_media'] = carousel_media if drop_incompat_keys: cls._drop_keys( media, [ '__typename', 'code', 'comments_disabled', 'date', 'dimensions', 'display_src', 'edge_sidecar_to_children', 'is_ad', 'is_video', 'owner', 'thumbnail_src', 'usertags', 'video_url', 'video_views', ]) cls._drop_keys( media.get('location'), ['lat', 'lng']) return media
[docs] @classmethod def comment(cls, comment, drop_incompat_keys=False): """Patch a comment object""" comment['created_time'] = str(int(comment['created_at'])) comment_user = comment.get('user') or comment.get('owner') from_user = { 'id': comment_user['id'], 'profile_picture': comment_user.get('profile_pic_url'), 'username': comment_user['username'], 'full_name': comment_user.get('full_name') or '' } comment['from'] = from_user if drop_incompat_keys: cls._drop_keys(comment, ['created_at', 'user']) return comment
[docs] @classmethod def user(cls, user, drop_incompat_keys=False): """Patch a user object""" user['bio'] = user['biography'] user['profile_picture'] = user['profile_pic_url'] user['website'] = user['external_url'] counts = { 'media': ( user.get('media', {}).get('count') or user.get('edge_owner_to_timeline_media', {}).get('count')), 'followed_by': ( user.get('followed_by', {}).get('count') or user.get('edge_followed_by', {}).get('count')), 'follows': ( user.get('follows', {}).get('count') or user.get('edge_follow', {}).get('count')), } user['counts'] = counts if drop_incompat_keys: cls._drop_keys( user, [ 'biography', 'external_url', 'followed_by', 'follows', 'media', 'profile_pic_url', 'status', ] ) return user
[docs] @classmethod def list_user(cls, user, drop_incompat_keys=False): """Patch a user list object""" user['profile_picture'] = user['profile_pic_url'] if drop_incompat_keys: cls._drop_keys( user, [ 'followed_by_viewer', 'is_verified', 'profile_pic_url', 'requested_by_viewer', ] ) return user