Compare commits

...
7 Commits
Author SHA1 Message Date
André Koch-Kramer ced65ea401 Release of version 3.3.6 2018-04-11 14:32:42 +02:00
André Koch-Kramer 73ec884ea4 Fix anonymous GraphQL queries
Closes #94.
2018-04-11 14:19:24 +02:00
Alexander Graf 74d5e35eb8 get_{comments,likes}: save requests
Post.get_{comments,likes} had a logic to save requests if comments/like
count is low. That logic had a bug that is now fixed.

Related to #94.
2018-04-11 11:24:49 +02:00
Alexander Graf 8612712f44 Release of version 3.3.5 2018-04-10 20:57:53 +02:00
Alexander Graf 67ac8f3397 Enforce being logged in for getting username by id
It seems it is not possible at the moment to get username by userid
anonymously.
2018-04-10 20:52:39 +02:00
Alexander Graf 5fe2a70374 Fix downloading of hashtags 2018-04-10 20:34:07 +02:00
Alexander Graf 8bd9c4449c Theme-related Documentation Enhancements
- Consistent background color in all types of codes
- Improve dd (list item) padding
- Link to Index in global TOC in navbar
- Briefly describe Instaloader in `index.html` title
- Remove page contents (navbartoc) in index, genindex and installation
- Link to new pypi.org rather than old pypi.python.org
2018-04-09 16:07:08 +02:00
7 changed files with 105 additions and 40 deletions
+10 -1
View File
@@ -1,3 +1,12 @@
code { code {
color: #222; color: #222;
} background-color: #f5f5f5;
}
dd {
padding-left: 8px;
}
dl.option dd {
padding-left: 12px;
}
+14
View File
@@ -0,0 +1,14 @@
<li class="dropdown globaltoc-container">
<a role="button"
id="dLabelGlobalToc"
data-toggle="dropdown"
data-target="#"
href="{{ pathto(master_doc) }}">{{ theme_navbar_site_name }} <b class="caret"></b></a>
<ul class="dropdown-menu globaltoc"
role="menu"
aria-labelledby="dLabelGlobalToc">
{{ toctree(maxdepth=theme_globaltoc_depth|toint, collapse=False, includehidden=theme_globaltoc_includehidden|tobool) }}
<li role="separator" class="divider"></li>
<li class="toctree-l1"><a class="reference internal" href="genindex.html">Index</a></li>
</ul>
</li>
+7
View File
@@ -1,4 +1,11 @@
{% extends "!layout.html" %} {% extends "!layout.html" %}
{% block htmltitle %}
{% if pagename == "index" %}
<title>{{ title|striptags|e }}</title>
{% else %}
<title>{{ title|striptags|e }}{{ titlesuffix }}</title>
{% endif %}
{% endblock %}
{% block extrahead %} {% block extrahead %}
{{ super() }} {{ super() }}
<meta name="google-site-verification" content="FNUsDdkOJM9obm9QEdrNBVd9AtAzHzA-aaSMCUrS6C8" /> <meta name="google-site-verification" content="FNUsDdkOJM9obm9QEdrNBVd9AtAzHzA-aaSMCUrS6C8" />
+3 -3
View File
@@ -14,7 +14,7 @@
{% if theme_navbar_title -%}{{ theme_navbar_title|e }}{%- else -%}{{ project|e }}{%- endif -%} {% if theme_navbar_title -%}{{ theme_navbar_title|e }}{%- else -%}{{ project|e }}{%- endif -%}
</a> </a>
<span class="navbar-text navbar-version pull-left"> <span class="navbar-text navbar-version pull-left">
<a href="https://github.com/instaloader/instaloader/releases/tag/v{{current_release}}" title="Released on {{current_release_date}}.">v<b>{{current_release}}</b></a> <a href="https://github.com/instaloader/instaloader/releases/tag/v{{current_release}}" title="Released on {{current_release_date}}">v<b>{{current_release}}</b></a>
</span> </span>
</div> </div>
@@ -27,7 +27,7 @@
{% endif %} {% endif %}
{% block navbartoc %} {% block navbartoc %}
{% include "globaltoc.html" %} {% include "globaltoc.html" %}
{% if theme_navbar_pagenav %} {% if theme_navbar_pagenav and pagename not in ("index", "genindex", "installation") %}
{% include "navbartoc.html" %} {% include "navbartoc.html" %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}
@@ -46,7 +46,7 @@
<ul class="dropdown-menu" role="menu" aria-labelledby="dLabelUsefulLinks"> <ul class="dropdown-menu" role="menu" aria-labelledby="dLabelUsefulLinks">
<li><a href="https://github.com/instaloader/instaloader/issues">Issue Tracker</a></li> <li><a href="https://github.com/instaloader/instaloader/issues">Issue Tracker</a></li>
<li><a href="https://github.com/instaloader/instaloader/releases">Version History</a></li> <li><a href="https://github.com/instaloader/instaloader/releases">Version History</a></li>
<li><a href="https://pypi.python.org/pypi/instaloader">Instaloader on PyPI</a></li> <li><a href="https://pypi.org/project/instaloader/">Instaloader on PyPI</a></li>
<li><a href="https://github.com/instaloader/instaloader">Instaloader on GitHub</a></li> <li><a href="https://github.com/instaloader/instaloader">Instaloader on GitHub</a></li>
<li role="separator" class="divider"></li> <li role="separator" class="divider"></li>
<li style="padding-left: 20px;"><a class="github-button" href="https://github.com/instaloader/instaloader" data-icon="octicon-star" data-size="large" data-show-count="true" aria-label="Star Instaloader on GitHub">Star</a></li> <li style="padding-left: 20px;"><a class="github-button" href="https://github.com/instaloader/instaloader" data-icon="octicon-star" data-size="large" data-show-count="true" aria-label="Star Instaloader on GitHub">Star</a></li>
+1 -1
View File
@@ -212,7 +212,7 @@ html_domain_indices = False
# If false, no index is generated. # If false, no index is generated.
# #
html_use_index = False html_use_index = True
# If true, the index is split into individual pages for each letter. # If true, the index is split into individual pages for each letter.
# #
+5 -3
View File
@@ -5,6 +5,8 @@
saved media, and their metadata, comments and captions. saved media, and their metadata, comments and captions.
Written in Python. Written in Python.
.. title:: Instaloader — Download Instagram Photos and Metadata
Instaloader Instaloader
=========== ===========
@@ -43,8 +45,8 @@ With `Python <https://www.python.org/>`__ installed, do::
:stories | :feed | :saved :stories | :feed | :saved
Table of Contents Instaloader Documentation
----------------- -------------------------
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
@@ -59,7 +61,7 @@ Useful Links
------------ ------------
- `Git Repository (on GitHub) <https://github.com/instaloader/instaloader>`__ - `Git Repository (on GitHub) <https://github.com/instaloader/instaloader>`__
- `PyPI Project Page <https://pypi.python.org/pypi/instaloader>`__ - `PyPI Project Page <https://pypi.org/project/instaloader/>`__
- `Issue Tracker / Bug Tracker <https://github.com/instaloader/instaloader/issues>`__ - `Issue Tracker / Bug Tracker <https://github.com/instaloader/instaloader/issues>`__
- `Version History <https://github.com/instaloader/instaloader/releases>`__ - `Version History <https://github.com/instaloader/instaloader/releases>`__
+65 -32
View File
@@ -3,6 +3,7 @@
"""Download pictures (or videos) along with their captions and other metadata from Instagram.""" """Download pictures (or videos) along with their captions and other metadata from Instagram."""
import ast import ast
import getpass import getpass
import hashlib
import json import json
import os import os
import pickle import pickle
@@ -30,7 +31,7 @@ import requests.utils
import urllib3 import urllib3
__version__ = '3.3.4' __version__ = '3.3.6'
# NOTE: duplicated in README.rst and docs/index.rst # NOTE: duplicated in README.rst and docs/index.rst
USAGE_STRING = """ USAGE_STRING = """
@@ -201,6 +202,7 @@ class Post:
self._profile = profile self._profile = profile
self._profile_id = profile_id self._profile_id = profile_id
self._full_metadata_dict = None self._full_metadata_dict = None
self._rhx_gis = None
@classmethod @classmethod
def from_shortcode(cls, instaloader: 'Instaloader', shortcode: str): def from_shortcode(cls, instaloader: 'Instaloader', shortcode: str):
@@ -239,11 +241,9 @@ class Post:
@property @property
def _full_metadata(self) -> Dict[str, Any]: def _full_metadata(self) -> Dict[str, Any]:
if not self._full_metadata_dict: if not self._full_metadata_dict:
pic_json = self._instaloader.get_json("p/{0}/".format(self.shortcode), params={'__a': 1}) pic_json = self._instaloader.get_json("p/{0}/".format(self.shortcode), params={})
if "graphql" in pic_json: self._full_metadata_dict = pic_json['entry_data']['PostPage'][0]['graphql']['shortcode_media']
self._full_metadata_dict = pic_json["graphql"]["shortcode_media"] self._rhx_gis = pic_json['rhx_gis']
else:
self._full_metadata_dict = pic_json["media"]
return self._full_metadata_dict return self._full_metadata_dict
def _field(self, *keys) -> Any: def _field(self, *keys) -> Any:
@@ -384,9 +384,11 @@ class Post:
if self.comments == len(comment_edges): if self.comments == len(comment_edges):
# If the Post's metadata already contains all comments, don't do GraphQL requests to obtain them # If the Post's metadata already contains all comments, don't do GraphQL requests to obtain them
yield from (comment['node'] for comment in comment_edges) yield from (comment['node'] for comment in comment_edges)
return
yield from self._instaloader.graphql_node_list(17852405266163336, {'shortcode': self.shortcode}, yield from self._instaloader.graphql_node_list(17852405266163336, {'shortcode': self.shortcode},
'https://www.instagram.com/p/' + self.shortcode + '/', 'https://www.instagram.com/p/' + self.shortcode + '/',
lambda d: d['data']['shortcode_media']['edge_media_to_comment']) lambda d: d['data']['shortcode_media']['edge_media_to_comment'],
rhx_gis=self._rhx_gis)
def get_likes(self) -> Iterator[Dict[str, Any]]: def get_likes(self) -> Iterator[Dict[str, Any]]:
"""Iterate over all likes of the post. """Iterate over all likes of the post.
@@ -401,9 +403,11 @@ class Post:
if self.likes == len(likes_edges): if self.likes == len(likes_edges):
# If the Post's metadata already contains all likes, don't do GraphQL requests to obtain them # If the Post's metadata already contains all likes, don't do GraphQL requests to obtain them
yield from (like['node'] for like in likes_edges) yield from (like['node'] for like in likes_edges)
return
yield from self._instaloader.graphql_node_list("1cb6ec562846122743b61e492c85999f", {'shortcode': self.shortcode}, yield from self._instaloader.graphql_node_list("1cb6ec562846122743b61e492c85999f", {'shortcode': self.shortcode},
'https://www.instagram.com/p/' + self.shortcode + '/', 'https://www.instagram.com/p/' + self.shortcode + '/',
lambda d: d['data']['shortcode_media']['edge_liked_by']) lambda d: d['data']['shortcode_media']['edge_liked_by'],
rhx_gis=self._rhx_gis)
def get_location(self) -> Optional[Dict[str, str]]: def get_location(self) -> Optional[Dict[str, str]]:
"""If the Post has a location, returns a dictionary with fields 'lat' and 'lng'.""" """If the Post has a location, returns a dictionary with fields 'lat' and 'lng'."""
@@ -600,7 +604,7 @@ class Instaloader:
:raises QueryReturnedNotFoundException: When the server responds with a 404. :raises QueryReturnedNotFoundException: When the server responds with a 404.
:raises ConnectionException: When query repeatedly failed. :raises ConnectionException: When query repeatedly failed.
""" """
def graphql_query_waittime(query_id: int, untracked_queries: bool = False) -> int: def graphql_query_waittime(query_id: Union[int, str], untracked_queries: bool = False) -> int:
sliding_window = 660 sliding_window = 660
timestamps = self.previous_queries.get(query_id) timestamps = self.previous_queries.get(query_id)
if not timestamps: if not timestamps:
@@ -611,9 +615,9 @@ class Instaloader:
if len(timestamps) < 100 and not untracked_queries: if len(timestamps) < 100 and not untracked_queries:
return 0 return 0
return round(min(timestamps) + sliding_window - current_time) + 6 return round(min(timestamps) + sliding_window - current_time) + 6
is_graphql_query = 'query_id' in params and 'graphql/query' in path is_graphql_query = 'graphql/query' in path
if is_graphql_query: if is_graphql_query:
query_id = params['query_id'] query_id = params['query_id'] if 'query_id' in params else params['query_hash']
waittime = graphql_query_waittime(query_id) waittime = graphql_query_waittime(query_id)
if waittime > 0: if waittime > 0:
self._log('\nToo many queries in the last time. Need to wait {} seconds.'.format(waittime)) self._log('\nToo many queries in the last time. Need to wait {} seconds.'.format(waittime))
@@ -633,7 +637,13 @@ class Instaloader:
raise TooManyRequests("429 - Too Many Requests") raise TooManyRequests("429 - Too Many Requests")
if resp.status_code != 200: if resp.status_code != 200:
raise ConnectionException("HTTP error code {}.".format(resp.status_code)) raise ConnectionException("HTTP error code {}.".format(resp.status_code))
resp_json = resp.json() if not is_graphql_query and not "__a" in params and host == "www.instagram.com":
match = re.search(r'window\._sharedData = (.*);</script>', resp.text)
if match is None:
raise ConnectionException("Could not find \"window._sharedData\" in html response.")
return json.loads(match.group(1))
else:
resp_json = resp.json()
if 'status' in resp_json and resp_json['status'] != "ok": if 'status' in resp_json and resp_json['status'] != "ok":
if 'message' in resp_json: if 'message' in resp_json:
raise ConnectionException("Returned \"{}\" status, message \"{}\".".format(resp_json['status'], raise ConnectionException("Returned \"{}\" status, message \"{}\".".format(resp_json['status'],
@@ -693,7 +703,7 @@ class Instaloader:
return session return session
def graphql_query(self, query_identifier: Union[int, str], variables: Dict[str, Any], def graphql_query(self, query_identifier: Union[int, str], variables: Dict[str, Any],
referer: Optional[str] = None) -> Dict[str, Any]: referer: Optional[str] = None, rhx_gis: Optional[str] = None) -> Dict[str, Any]:
""" """
Do a GraphQL Query. Do a GraphQL Query.
@@ -711,9 +721,18 @@ class Instaloader:
tmpsession.headers['accept'] = '*/*' tmpsession.headers['accept'] = '*/*'
if referer is not None: if referer is not None:
tmpsession.headers['referer'] = urllib.parse.quote(referer) tmpsession.headers['referer'] = urllib.parse.quote(referer)
variables_json = json.dumps(variables, separators=(',', ':'))
if rhx_gis:
values = "{}:{}:{}:{}".format(rhx_gis, tmpsession.cookies['csrftoken'], self.user_agent, variables_json)
x_instagram_gis = hashlib.md5(values.encode()).hexdigest()
tmpsession.cookies.set('ig_pr', '2')
tmpsession.headers['x-instagram-gis'] = x_instagram_gis
resp_json = self.get_json('graphql/query', resp_json = self.get_json('graphql/query',
params={'query_id' if isinstance(query_identifier, int) else 'query_hash': query_identifier, params={'query_id' if isinstance(query_identifier, int) else 'query_hash': query_identifier,
'variables': json.dumps(variables, separators=(',', ':'))}, 'variables': variables_json},
session=tmpsession) session=tmpsession)
if 'status' not in resp_json: if 'status' not in resp_json:
self.error("GraphQL response did not contain a \"status\" field.") self.error("GraphQL response did not contain a \"status\" field.")
@@ -738,20 +757,21 @@ class Instaloader:
def get_id_by_username(self, profile: str) -> int: def get_id_by_username(self, profile: str) -> int:
"""Each Instagram profile has its own unique ID which stays unmodified even if a user changes """Each Instagram profile has its own unique ID which stays unmodified even if a user changes
his/her username. To get said ID, given the profile's name, you may call this function.""" his/her username. To get said ID, given the profile's name, you may call this function."""
return int(self.get_profile_metadata(profile)['user']['id']) return int(self.get_profile_metadata(profile)[0]['user']['id'])
def graphql_node_list(self, query_identifier: Union[int, str], query_variables: Dict[str, Any], def graphql_node_list(self, query_identifier: Union[int, str], query_variables: Dict[str, Any],
query_referer: Optional[str], query_referer: Optional[str],
edge_extractor: Callable[[Dict[str, Any]], Dict[str, Any]]) -> Iterator[Dict[str, Any]]: edge_extractor: Callable[[Dict[str, Any]], Dict[str, Any]],
rhx_gis: Optional[str] = None) -> Iterator[Dict[str, Any]]:
"""Retrieve a list of GraphQL nodes.""" """Retrieve a list of GraphQL nodes."""
query_variables['first'] = Instaloader.GRAPHQL_PAGE_LENGTH query_variables['first'] = Instaloader.GRAPHQL_PAGE_LENGTH
data = self.graphql_query(query_identifier, query_variables, query_referer) data = self.graphql_query(query_identifier, query_variables, query_referer, rhx_gis)
while True: while True:
edge_struct = edge_extractor(data) edge_struct = edge_extractor(data)
yield from [edge['node'] for edge in edge_struct['edges']] yield from [edge['node'] for edge in edge_struct['edges']]
if edge_struct['page_info']['has_next_page']: if edge_struct['page_info']['has_next_page']:
query_variables['after'] = edge_struct['page_info']['end_cursor'] query_variables['after'] = edge_struct['page_info']['end_cursor']
data = self.graphql_query(query_identifier, query_variables, query_referer) data = self.graphql_query(query_identifier, query_variables, query_referer, rhx_gis)
else: else:
break break
@@ -1255,7 +1275,7 @@ class Instaloader:
if not self.is_logged_in: if not self.is_logged_in:
return return
data = self.get_profile_metadata(self.username) data, full_metadata = self.get_profile_metadata(self.username)
user_id = data["user"]["id"] user_id = data["user"]["id"]
while True: while True:
@@ -1275,7 +1295,8 @@ class Instaloader:
break break
data = self.graphql_query("f883d95537fbcd400f466f63d42bd8a1", data = self.graphql_query("f883d95537fbcd400f466f63d42bd8a1",
{'id': user_id, 'first': Instaloader.GRAPHQL_PAGE_LENGTH, {'id': user_id, 'first': Instaloader.GRAPHQL_PAGE_LENGTH,
'after': saved_media["page_info"]["end_cursor"]})['data'] 'after': saved_media["page_info"]["end_cursor"]},
rhx_gis=full_metadata['rhx_gis'])['data']
def download_saved_posts(self, max_count: int = None, fast_update: bool = False, def download_saved_posts(self, max_count: int = None, fast_update: bool = False,
filter_func: Optional[Callable[[Post], bool]] = None) -> None: filter_func: Optional[Callable[[Post], bool]] = None) -> None:
@@ -1302,10 +1323,19 @@ class Instaloader:
def get_hashtag_posts(self, hashtag: str) -> Iterator[Post]: def get_hashtag_posts(self, hashtag: str) -> Iterator[Post]:
"""Get Posts associated with a #hashtag.""" """Get Posts associated with a #hashtag."""
yield from (Post(self, node) for node in has_next_page = True
self.graphql_node_list(17875800862117404, {'tag_name': hashtag}, end_cursor = None
'https://www.instagram.com/explore/tags/{0}/'.format(hashtag), while has_next_page:
lambda d: d['data']['hashtag']['edge_hashtag_to_media'])) if end_cursor:
params = {'__a': 1, 'max_id': end_cursor}
else:
params = {'__a': 1}
hashtag_data = self.get_json('explore/tags/{0}/'.format(hashtag),
params)['graphql']['hashtag']['edge_hashtag_to_media']
yield from (Post(self, edge['node']) for edge in
hashtag_data['edges'])
has_next_page = hashtag_data['page_info']['has_next_page']
end_cursor = hashtag_data['page_info']['end_cursor']
def download_hashtag(self, hashtag: str, def download_hashtag(self, hashtag: str,
max_count: Optional[int] = None, max_count: Optional[int] = None,
@@ -1364,6 +1394,9 @@ class Instaloader:
self._log("Profile {0} does not match the stored unique ID {1}.".format(profile, profile_id)) self._log("Profile {0} does not match the stored unique ID {1}.".format(profile, profile_id))
else: else:
self._log("Trying to find profile {0} using its unique ID {1}.".format(profile, profile_id)) self._log("Trying to find profile {0} using its unique ID {1}.".format(profile, profile_id))
if not self.is_logged_in:
self.error("Profile {} changed its name. If you use --login=USERNAME, I can find out the new name.")
raise LoginRequiredException("Login required to obtain username from userid")
newname = self.get_username_by_id(profile_id) newname = self.get_username_by_id(profile_id)
self._log("Profile {0} has changed its name to {1}.".format(profile, newname)) self._log("Profile {0} has changed its name to {1}.".format(profile, newname))
if ((format_string_contains_key(self.dirname_pattern, 'profile') or if ((format_string_contains_key(self.dirname_pattern, 'profile') or
@@ -1389,15 +1422,15 @@ class Instaloader:
return profile, profile_id return profile, profile_id
raise ProfileNotExistsException("Profile {0} does not exist.".format(profile)) raise ProfileNotExistsException("Profile {0} does not exist.".format(profile))
def get_profile_metadata(self, profile_name: str) -> Dict[str, Any]: def get_profile_metadata(self, profile_name: str) -> Tuple[Dict[str, Any], Dict[str, Any]]:
"""Retrieves a profile's metadata, for use with e.g. :meth:`get_profile_posts` and :meth:`check_profile_id`.""" """Retrieves a profile's metadata, for use with e.g. :meth:`get_profile_posts` and :meth:`check_profile_id`."""
try: try:
metadata = self.get_json('{}/'.format(profile_name), params={'__a': 1}) metadata = self.get_json('{}/'.format(profile_name), params={})
return metadata['graphql'] if 'graphql' in metadata else metadata return metadata['entry_data']['ProfilePage'][0]['graphql'], metadata
except QueryReturnedNotFoundException: except QueryReturnedNotFoundException:
raise ProfileNotExistsException('Profile {} does not exist.'.format(profile_name)) raise ProfileNotExistsException('Profile {} does not exist.'.format(profile_name))
def get_profile_posts(self, profile_metadata: Dict[str, Any]) -> Iterator[Post]: def get_profile_posts(self, profile_metadata: Dict[str, Any], rhx_gis: str) -> Iterator[Post]:
"""Retrieve all posts from a profile.""" """Retrieve all posts from a profile."""
profile_name = profile_metadata['user']['username'] profile_name = profile_metadata['user']['username']
profile_id = int(profile_metadata['user']['id']) profile_id = int(profile_metadata['user']['id'])
@@ -1418,7 +1451,7 @@ class Instaloader:
data = self.graphql_query(17888483320059182, {'id': profile_metadata['user']['id'], data = self.graphql_query(17888483320059182, {'id': profile_metadata['user']['id'],
'first': Instaloader.GRAPHQL_PAGE_LENGTH, 'first': Instaloader.GRAPHQL_PAGE_LENGTH,
'after': end_cursor}, 'after': end_cursor},
'https://www.instagram.com/{0}/'.format(profile_name)) referer='https://www.instagram.com/{0}/'.format(profile_name), rhx_gis=rhx_gis)
media = data['data']['user']['edge_owner_to_timeline_media'] media = data['data']['user']['edge_owner_to_timeline_media']
yield from (Post(self, edge['node'], profile=profile_name, profile_id=profile_id) yield from (Post(self, edge['node'], profile=profile_name, profile_id=profile_id)
for edge in media['edges']) for edge in media['edges'])
@@ -1438,14 +1471,14 @@ class Instaloader:
with suppress(ProfileNotExistsException): with suppress(ProfileNotExistsException):
# ProfileNotExistsException is raised again later in check_profile_id() when we search the profile, so we # ProfileNotExistsException is raised again later in check_profile_id() when we search the profile, so we
# must suppress it here. # must suppress it here.
profile_metadata = self.get_profile_metadata(name) profile_metadata, full_metadata = self.get_profile_metadata(name)
# check if profile does exist or name has changed since last download # check if profile does exist or name has changed since last download
# and update name and json data if necessary # and update name and json data if necessary
name_updated, profile_id = self.check_profile_id(name, profile_metadata) name_updated, profile_id = self.check_profile_id(name, profile_metadata)
if name_updated != name: if name_updated != name:
name = name_updated name = name_updated
profile_metadata = self.get_profile_metadata(name) profile_metadata, full_metadata = self.get_profile_metadata(name)
# Download profile picture # Download profile picture
if profile_pic or profile_pic_only: if profile_pic or profile_pic_only:
@@ -1480,7 +1513,7 @@ class Instaloader:
else: else:
totalcount = profile_metadata["user"]["edge_owner_to_timeline_media"]["count"] totalcount = profile_metadata["user"]["edge_owner_to_timeline_media"]["count"]
count = 1 count = 1
for post in self.get_profile_posts(profile_metadata): for post in self.get_profile_posts(profile_metadata, rhx_gis=full_metadata['rhx_gis']):
self._log("[%3i/%3i] " % (count, totalcount), end="", flush=True) self._log("[%3i/%3i] " % (count, totalcount), end="", flush=True)
count += 1 count += 1
if filter_func is not None and not filter_func(post): if filter_func is not None and not filter_func(post):