Various minor fixes
* Fix Post.owner_profile in cases where initial owner struct contains id only * Assert node structure is complete enough at Post creation * Fix get_followees() call in _main() * Prefer importing from '.' rather than '.<submodule>' in __main__ * Fix name of instaloader_unittests ('-' is illegal in module names) * Foreport5fe2a70374
"Fix downloading of hashtags" * Foreport67ac8f3397
"Enforce being logged in for getting username by id"
This commit is contained in:
parent
5b9590a768
commit
25b8165547
@ -6,11 +6,10 @@ import sys
|
||||
from argparse import ArgumentParser, SUPPRESS
|
||||
from typing import Callable, List, Optional
|
||||
|
||||
from . import __version__
|
||||
from .exceptions import *
|
||||
from .instaloader import Instaloader, Tristate, get_default_session_filename
|
||||
from . import (Instaloader, InstaloaderException, InvalidArgumentException, Post, Profile, ProfileNotExistsException,
|
||||
Tristate, __version__)
|
||||
from .instaloader import get_default_session_filename
|
||||
from .instaloadercontext import default_user_agent
|
||||
from .structures import Post
|
||||
|
||||
|
||||
def usage_string():
|
||||
@ -92,7 +91,8 @@ def _main(instaloader: Instaloader, targetlist: List[str],
|
||||
with instaloader.context.error_catcher(target):
|
||||
if target[0] == '@':
|
||||
instaloader.context.log("Retrieving followees of %s..." % target[1:])
|
||||
profiles.update([followee['username'] for followee in instaloader.get_followees(target[1:])])
|
||||
followees = instaloader.get_followees(Profile.from_username(instaloader.context, target[1:]))
|
||||
profiles.update([followee['username'] for followee in followees])
|
||||
elif target[0] == '#':
|
||||
instaloader.download_hashtag(hashtag=target[1:], max_count=max_count, fast_update=fast_update,
|
||||
filter_func=filter_func)
|
||||
|
@ -607,12 +607,18 @@ class Instaloader:
|
||||
|
||||
def get_hashtag_posts(self, hashtag: str) -> Iterator[Post]:
|
||||
"""Get Posts associated with a #hashtag."""
|
||||
yield from (Post(self.context, node)
|
||||
for node in self.context.graphql_node_list("298b92c8d7cad703f7565aa892ede943",
|
||||
{'tag_name': hashtag},
|
||||
'https://www.instagram.com/explore/tags/{0}/'
|
||||
.format(hashtag),
|
||||
lambda d: d['data']['hashtag']['edge_hashtag_to_media']))
|
||||
has_next_page = True
|
||||
end_cursor = None
|
||||
while has_next_page:
|
||||
if end_cursor:
|
||||
params = {'__a': 1, 'max_id': end_cursor}
|
||||
else:
|
||||
params = {'__a': 1}
|
||||
hashtag_data = self.context.get_json('explore/tags/{0}/'.format(hashtag),
|
||||
params)['graphql']['hashtag']['edge_hashtag_to_media']
|
||||
yield from (Post(self.context, 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,
|
||||
max_count: Optional[int] = None,
|
||||
@ -675,6 +681,10 @@ class Instaloader:
|
||||
else:
|
||||
self.context.log("Trying to find profile {0} using its unique ID {1}.".format(profile_name,
|
||||
profile_id))
|
||||
if not self.context.is_logged_in:
|
||||
self.context.error("Profile {} changed its name. "
|
||||
"If you use --login=USERNAME, I can find out the new name.")
|
||||
raise LoginRequiredException("--login=USERNAME required to obtain profile name from its ID number")
|
||||
profile_from_id = Profile.from_id(self.context, profile_id)
|
||||
newname = profile_from_id.username
|
||||
self.context.log("Profile {0} has changed its name to {1}.".format(profile_name, newname))
|
||||
|
@ -43,7 +43,15 @@ class Post:
|
||||
:param node: Node structure, as returned by Instagram.
|
||||
:param owner_profile: The Profile of the owner, if already known at creation.
|
||||
"""
|
||||
assert 'shortcode' in node
|
||||
|
||||
# Ensure node contains all the data that is accessed via self._node
|
||||
assert 'shortcode' in node or 'code' in node
|
||||
assert 'id' in node
|
||||
assert 'owner' in node
|
||||
assert 'date' in node or 'taken_at_timestamp' in node
|
||||
assert 'display_url' in node or 'display_src' in node
|
||||
assert 'is_video' in node
|
||||
|
||||
self._context = context
|
||||
self._node = node
|
||||
self._owner_profile = owner_profile
|
||||
@ -109,10 +117,18 @@ class Post:
|
||||
@property
|
||||
def owner_profile(self) -> 'Profile':
|
||||
if not self._owner_profile:
|
||||
owner_struct = self._field('owner')
|
||||
if 'username' in self._node['owner']:
|
||||
owner_struct = self._node['owner']
|
||||
else:
|
||||
# Sometimes, the 'owner' structure does not contain the username, only the user's ID. In that case,
|
||||
# this call triggers downloading of the complete Post metadata struct, where the owner username
|
||||
# is contained. This is better than to get the username by user ID, since it is possible anonymously
|
||||
# and gives us other information that is more likely to be usable.
|
||||
owner_struct = self._full_metadata['owner']
|
||||
if 'username' in owner_struct:
|
||||
self._owner_profile = Profile(self._context, owner_struct)
|
||||
else:
|
||||
# Fallback, if we still did not get the owner username
|
||||
self._owner_profile = Profile.from_id(self._context, owner_struct['id'])
|
||||
return self._owner_profile
|
||||
|
||||
@ -303,6 +319,8 @@ class Profile:
|
||||
|
||||
@classmethod
|
||||
def from_id(cls, context: InstaloaderContext, profile_id: int):
|
||||
if not context.is_logged_in:
|
||||
raise LoginRequiredException("--login=USERNAME required to obtain profile metadata from its ID number.")
|
||||
data = context.graphql_query("472f257a40c653c64c666ce877d59d2b",
|
||||
{'id': str(profile_id), 'first': 1})['data']['user']
|
||||
if data:
|
||||
|
@ -91,6 +91,7 @@ class TestInstaloader(unittest.TestCase):
|
||||
print(f['username'])
|
||||
|
||||
def test_get_username_by_id(self):
|
||||
self.L.load_session_from_file(OWN_USERNAME)
|
||||
self.assertEqual(PUBLIC_PROFILE.lower(),
|
||||
instaloader.Profile.from_id(self.L.context, PUBLIC_PROFILE_ID).username)
|
||||
|
Loading…
Reference in New Issue
Block a user