mirror of
https://github.com/instaloader/instaloader.git
synced 2024-08-18 20:59:38 +02:00
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 argparse import ArgumentParser, SUPPRESS
|
||||||
from typing import Callable, List, Optional
|
from typing import Callable, List, Optional
|
||||||
|
|
||||||
from . import __version__
|
from . import (Instaloader, InstaloaderException, InvalidArgumentException, Post, Profile, ProfileNotExistsException,
|
||||||
from .exceptions import *
|
Tristate, __version__)
|
||||||
from .instaloader import Instaloader, Tristate, get_default_session_filename
|
from .instaloader import get_default_session_filename
|
||||||
from .instaloadercontext import default_user_agent
|
from .instaloadercontext import default_user_agent
|
||||||
from .structures import Post
|
|
||||||
|
|
||||||
|
|
||||||
def usage_string():
|
def usage_string():
|
||||||
@ -92,7 +91,8 @@ def _main(instaloader: Instaloader, targetlist: List[str],
|
|||||||
with instaloader.context.error_catcher(target):
|
with instaloader.context.error_catcher(target):
|
||||||
if target[0] == '@':
|
if target[0] == '@':
|
||||||
instaloader.context.log("Retrieving followees of %s..." % target[1:])
|
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] == '#':
|
elif target[0] == '#':
|
||||||
instaloader.download_hashtag(hashtag=target[1:], max_count=max_count, fast_update=fast_update,
|
instaloader.download_hashtag(hashtag=target[1:], max_count=max_count, fast_update=fast_update,
|
||||||
filter_func=filter_func)
|
filter_func=filter_func)
|
||||||
|
@ -607,12 +607,18 @@ 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.context, node)
|
has_next_page = True
|
||||||
for node in self.context.graphql_node_list("298b92c8d7cad703f7565aa892ede943",
|
end_cursor = None
|
||||||
{'tag_name': hashtag},
|
while has_next_page:
|
||||||
'https://www.instagram.com/explore/tags/{0}/'
|
if end_cursor:
|
||||||
.format(hashtag),
|
params = {'__a': 1, 'max_id': end_cursor}
|
||||||
lambda d: d['data']['hashtag']['edge_hashtag_to_media']))
|
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,
|
def download_hashtag(self, hashtag: str,
|
||||||
max_count: Optional[int] = None,
|
max_count: Optional[int] = None,
|
||||||
@ -675,6 +681,10 @@ class Instaloader:
|
|||||||
else:
|
else:
|
||||||
self.context.log("Trying to find profile {0} using its unique ID {1}.".format(profile_name,
|
self.context.log("Trying to find profile {0} using its unique ID {1}.".format(profile_name,
|
||||||
profile_id))
|
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)
|
profile_from_id = Profile.from_id(self.context, profile_id)
|
||||||
newname = profile_from_id.username
|
newname = profile_from_id.username
|
||||||
self.context.log("Profile {0} has changed its name to {1}.".format(profile_name, newname))
|
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 node: Node structure, as returned by Instagram.
|
||||||
:param owner_profile: The Profile of the owner, if already known at creation.
|
: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._context = context
|
||||||
self._node = node
|
self._node = node
|
||||||
self._owner_profile = owner_profile
|
self._owner_profile = owner_profile
|
||||||
@ -109,10 +117,18 @@ class Post:
|
|||||||
@property
|
@property
|
||||||
def owner_profile(self) -> 'Profile':
|
def owner_profile(self) -> 'Profile':
|
||||||
if not self._owner_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:
|
if 'username' in owner_struct:
|
||||||
self._owner_profile = Profile(self._context, owner_struct)
|
self._owner_profile = Profile(self._context, owner_struct)
|
||||||
else:
|
else:
|
||||||
|
# Fallback, if we still did not get the owner username
|
||||||
self._owner_profile = Profile.from_id(self._context, owner_struct['id'])
|
self._owner_profile = Profile.from_id(self._context, owner_struct['id'])
|
||||||
return self._owner_profile
|
return self._owner_profile
|
||||||
|
|
||||||
@ -303,6 +319,8 @@ class Profile:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_id(cls, context: InstaloaderContext, profile_id: int):
|
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",
|
data = context.graphql_query("472f257a40c653c64c666ce877d59d2b",
|
||||||
{'id': str(profile_id), 'first': 1})['data']['user']
|
{'id': str(profile_id), 'first': 1})['data']['user']
|
||||||
if data:
|
if data:
|
||||||
|
@ -91,6 +91,7 @@ class TestInstaloader(unittest.TestCase):
|
|||||||
print(f['username'])
|
print(f['username'])
|
||||||
|
|
||||||
def test_get_username_by_id(self):
|
def test_get_username_by_id(self):
|
||||||
|
self.L.load_session_from_file(OWN_USERNAME)
|
||||||
self.assertEqual(PUBLIC_PROFILE.lower(),
|
self.assertEqual(PUBLIC_PROFILE.lower(),
|
||||||
instaloader.Profile.from_id(self.L.context, PUBLIC_PROFILE_ID).username)
|
instaloader.Profile.from_id(self.L.context, PUBLIC_PROFILE_ID).username)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user