1
0
mirror of https://github.com/instaloader/instaloader.git synced 2024-07-04 10:57:58 +02:00

Merge branch 'master' into upcoming/v4.12

This commit is contained in:
Alexander Graf 2024-06-29 19:19:30 +02:00
commit 5e04063002
13 changed files with 77 additions and 68 deletions

View File

@ -8,13 +8,14 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: "Checkout Instaloader Repository" - name: "Checkout Instaloader Repository"
uses: actions/checkout@v2 uses: actions/checkout@v4
with: with:
fetch-depth: 0 # needed for building docs fetch-depth: 0 # needed for building docs
- name: "Setup Python" - name: "Setup Python"
uses: actions/setup-python@v2 uses: actions/setup-python@v5
with: with:
python-version: 3.12 python-version: 3.12
cache: 'pipenv'
- name: "Install Dependencies" - name: "Install Dependencies"
run: | run: |
python -m pip install pipenv==2023.12.1 python -m pip install pipenv==2023.12.1
@ -23,7 +24,7 @@ jobs:
run: pipenv --python `python --version | grep -Eo '3\.[0-9]+'` run make -C docs html SPHINXOPTS="-W -n" run: pipenv --python `python --version | grep -Eo '3\.[0-9]+'` run make -C docs html SPHINXOPTS="-W -n"
- name: "Deploy Documentation" - name: "Deploy Documentation"
if: github.event_name == 'push' && github.ref == 'refs/heads/master' if: github.event_name == 'push' && github.ref == 'refs/heads/master'
uses: JamesIves/github-pages-deploy-action@v4.2.5 uses: JamesIves/github-pages-deploy-action@v4
with: with:
branch: master branch: master
repository-name: instaloader/instaloader.github.io repository-name: instaloader/instaloader.github.io

View File

@ -12,11 +12,12 @@ jobs:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
steps: steps:
- name: Checkout Instaloader Repository - name: Checkout Instaloader Repository
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v2 uses: actions/setup-python@v5
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
cache: 'pipenv'
- name: Install Dependencies - name: Install Dependencies
run: | run: |
python -m pip install pipenv==2023.12.1 python -m pip install pipenv==2023.12.1

View File

@ -11,16 +11,16 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: "Checkout Repository" - name: "Checkout Repository"
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: "Setup Python" - name: "Setup Python"
uses: actions/setup-python@v2 uses: actions/setup-python@v5
with: with:
python-version: 3.12 python-version: 3.12
- name: "Get the tagged version" - name: "Get the tagged version"
id: get_version id: get_version
env: env:
GITHUB_REF: ${{ github.ref }} GITHUB_REF: ${{ github.ref }}
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\/v/} run: echo "VERSION=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_OUTPUT
shell: bash shell: bash
- name: "Install Dependencies" - name: "Install Dependencies"
run: python -m pip install setuptools run: python -m pip install setuptools

View File

@ -2,7 +2,7 @@ name: Mark stale issues and pull requests
on: on:
schedule: schedule:
- cron: "10 14 * * *" - cron: "55 7 * * *"
jobs: jobs:
stale: stale:
@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v3 - uses: actions/stale@v9
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
only-labels: 'question' only-labels: 'question'
@ -21,14 +21,14 @@ jobs:
days-before-stale: 21 days-before-stale: 21
days-before-close: -1 days-before-close: -1
remove-stale-when-updated: false remove-stale-when-updated: false
- uses: actions/stale@v1 - uses: actions/stale@v9
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'There has been no activity on this issue for an extended period of time. This issue will be closed after further 14 days of inactivity.' stale-issue-message: 'There has been no activity on this issue for an extended period of time. This issue will be closed after further 14 days of inactivity.'
stale-pr-message: 'There has been no activity on this Pull Request for an extended period of time. This Pull Request will be closed after further 14 days of inactivity.' stale-pr-message: 'There has been no activity on this Pull Request for an extended period of time. This Pull Request will be closed after further 14 days of inactivity.'
stale-issue-label: 'stale' stale-issue-label: 'stale'
stale-pr-label: 'stale' stale-pr-label: 'stale'
exempt-issue-label: 'leave open' exempt-issue-labels: 'leave open'
exempt-pr-label: 'leave open' exempt-pr-labels: 'leave open'
days-before-stale: 189 days-before-stale: 189
days-before-close: 14 days-before-close: 14

View File

@ -8,17 +8,18 @@ jobs:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- name: Checkout Instaloader repository - name: Checkout Instaloader repository
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v2 uses: actions/setup-python@v5
with: with:
python-version: "3.12" python-version: "3.12"
cache: 'pipenv'
architecture: x64 architecture: x64
- name: Get the tagged version - name: Get the tagged version
id: get_version id: get_version
env: env:
GITHUB_REF: ${{ github.ref }} GITHUB_REF: ${{ github.ref }}
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\/v/} run: echo "VERSION=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_OUTPUT
shell: bash shell: bash
- name: Run custom python script for EXE creation - name: Run custom python script for EXE creation
env: env:

View File

@ -75,7 +75,7 @@ What to Download
Instaloader supports the following targets: Instaloader supports the following targets:
- ``profile`` - ``profile``
Public profile, or private profile with :option:`--login`. Public profile, or private profile with :ref:`login<login>`.
If an already-downloaded profile has been renamed, Instaloader automatically If an already-downloaded profile has been renamed, Instaloader automatically
finds it by its unique ID and renames the folder accordingly. finds it by its unique ID and renames the folder accordingly.
@ -102,23 +102,23 @@ Instaloader supports the following targets:
Posts tagged with a given location; the location ID is the numerical ID Posts tagged with a given location; the location ID is the numerical ID
Instagram labels a location with (e.g. Instagram labels a location with (e.g.
\https://www.instagram.com/explore/locations/**362629379**/plymouth-naval-memorial/). \https://www.instagram.com/explore/locations/**362629379**/plymouth-naval-memorial/).
Requires :option:`--login`. Requires :ref:`login<login>`.
.. versionadded:: 4.2 .. versionadded:: 4.2
- ``:stories`` - ``:stories``
The currently-visible **stories** of your followees (requires The currently-visible **stories** of your followees (requires
:option:`--login`). :ref:`login<login>`).
- ``:feed`` - ``:feed``
Your **feed** (requires :option:`--login`). Your **feed** (requires :ref:`login<login>`).
- ``:saved`` - ``:saved``
Posts which are marked as **saved** (requires :option:`--login`). Posts which are marked as **saved** (requires :ref:`login<login>`).
- ``@profile`` - ``@profile``
All profiles that are followed by ``profile``, i.e. the *followees* of All profiles that are followed by ``profile``, i.e. the *followees* of
``profile`` (requires :option:`--login`). ``profile`` (requires :ref:`login<login>`).
- ``-post`` - ``-post``
Replace **post** with the post's shortcode to download single post. Must be preceded by ``--`` in Replace **post** with the post's shortcode to download single post. Must be preceded by ``--`` in
@ -140,7 +140,7 @@ downloads the pictures and videos and their captions. You can specify
- :option:`--geotags` - :option:`--geotags`
**download geotags** of each post and save them as **download geotags** of each post and save them as
Google Maps link (requires :option:`--login`), Google Maps link (requires :ref:`login<login>`),
For a reference of all supported command line options, see For a reference of all supported command line options, see
:ref:`command-line-options`. :ref:`command-line-options`.
@ -255,7 +255,7 @@ Id est, the following attributes can be used with both
As :option:`--post-filter`, the following attributes can be used additionally: As :option:`--post-filter`, the following attributes can be used additionally:
- :attr:`~Post.viewer_has_liked` (bool) - :attr:`~Post.viewer_has_liked` (bool)
Whether user (with :option:`--login`) has liked given post. To download the Whether user (with :ref:`login<login>`) has liked given post. To download the
pictures from your feed that you have liked:: pictures from your feed that you have liked::
instaloader --login=your_username --post-filter=viewer_has_liked :feed instaloader --login=your_username --post-filter=viewer_has_liked :feed
@ -307,7 +307,7 @@ the post's caption::
instaloader --post-metadata-txt="{likes} likes." <target> instaloader --post-metadata-txt="{likes} likes." <target>
Note that with this feature, it is possible to easily and fastly extract Note that with this feature, it is possible to easily and quickly extract
additional metadata of already-downloaded posts, by reimporting their JSON additional metadata of already-downloaded posts, by reimporting their JSON
files. Say, you now also want to export the number of comments the Posts had files. Say, you now also want to export the number of comments the Posts had
when they were downloaded:: when they were downloaded::

View File

@ -26,7 +26,7 @@ Targets
^^^^^^^ ^^^^^^^
Specify a list of targets. For each of these, Instaloader creates a folder and Specify a list of targets. For each of these, Instaloader creates a folder and
stores all posts along with the pictures's captions there. stores all posts along with the pictures' captions there.
.. include:: basic-usage.rst .. include:: basic-usage.rst
:start-after: targets-start :start-after: targets-start
@ -61,13 +61,13 @@ What to Download of each Post
Download geotags when available. Geotags are stored as a text file with Download geotags when available. Geotags are stored as a text file with
the location's name and a Google Maps link. This requires an additional the location's name and a Google Maps link. This requires an additional
request to the Instagram server for each picture. Requires :option:`--login`. request to the Instagram server for each picture. Requires :ref:`login<login>`.
.. option:: --comments, -C .. option:: --comments, -C
Download and update comments for each post. This requires an additional Download and update comments for each post. This requires an additional
request to the Instagram server for each post, which is why it is disabled by request to the Instagram server for each post, which is why it is disabled by
default. default. Requires :ref:`login<login>`.
.. option:: --no-captions .. option:: --no-captions
@ -116,12 +116,12 @@ What to Download of each Profile
.. option:: --stories, -s .. option:: --stories, -s
Also download stories of each profile that is downloaded. Requires Also download stories of each profile that is downloaded. Requires
:option:`--login`. :ref:`login<login>`.
.. option:: --highlights .. option:: --highlights
Also download highlights of each profile that is downloaded. Requires Also download highlights of each profile that is downloaded. Requires
:option:`--login`. :ref:`login<login>`.
.. versionadded:: 4.1 .. versionadded:: 4.1
@ -183,6 +183,7 @@ Which Posts to Download
Do not attempt to download more than COUNT posts. Applies to Do not attempt to download more than COUNT posts. Applies to
``#hashtag``, ``%location_id``, ``:feed``, and ``:saved``. ``#hashtag``, ``%location_id``, ``:feed``, and ``:saved``.
.. _login:
Login (Download Private Profiles) Login (Download Private Profiles)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -192,6 +193,9 @@ profiles. To login, pass the :option:`--login` option. Your session cookie (not
password!) will be saved to a local file to be reused next time you want password!) will be saved to a local file to be reused next time you want
Instaloader to login. Instaloader to login.
Instead of :option:`--login`, it is possible to use
:option:`--load-cookies` to import a session from a browser.
.. option:: --login YOUR-USERNAME, -l YOUR-USERNAME .. option:: --login YOUR-USERNAME, -l YOUR-USERNAME
Login name (profile name) for your Instagram account. Login name (profile name) for your Instagram account.
@ -204,8 +208,8 @@ Instaloader to login.
Incompatible with :option:`--login` due to potential username mismatch between user input and browser login. Incompatible with :option:`--login` due to potential username mismatch between user input and browser login.
Supported browsers: Brave, Chrome, Chromium, Edge, Firefox, LibreWolf, Opera, Opera_GX, Safari and Vivaldi. Supported browsers: Brave, Chrome, Chromium, Edge, Firefox, LibreWolf, Opera, Opera_GX, Safari and Vivaldi.
After loading the cookies run the :option:`--login` option as it is required to download high quality media In subsequent runs, you can just use :option:`--login` to reuse the
and to make full use of Instaloader's features. same session, which is saved by Instaloader.
.. versionadded:: 4.11 .. versionadded:: 4.11

View File

@ -33,7 +33,7 @@ happen under normal conditions, consider adjusting the
There have been observations that services, that in their nature offer There have been observations that services, that in their nature offer
promiscuous IP addresses, such as cloud, VPN and public proxy services, might be promiscuous IP addresses, such as cloud, VPN and public proxy services, might be
subject to significantly stricter limits for anonymous access. However, subject to significantly stricter limits for anonymous access. However,
logged-in accesses (see :option:`--login`) do not seem to be affected. :ref:`logged-in accesses<login>` do not seem to be affected.
Instaloader allows to adjust the rate controlling behavior by overriding Instaloader allows to adjust the rate controlling behavior by overriding
:class:`instaloader.RateController`. :class:`instaloader.RateController`.

View File

@ -1,7 +1,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."""
__version__ = '4.11' __version__ = '4.11.1'
try: try:

View File

@ -5,7 +5,6 @@ import datetime
import os import os
import re import re
import sys import sys
import textwrap
from argparse import ArgumentParser, ArgumentTypeError, SUPPRESS from argparse import ArgumentParser, ArgumentTypeError, SUPPRESS
from enum import IntEnum from enum import IntEnum
from typing import List, Optional from typing import List, Optional
@ -130,9 +129,7 @@ def import_session(browser, instaloader, cookiefile):
raise LoginException(f"Not logged in. Are you logged in successfully in {browser}?") raise LoginException(f"Not logged in. Are you logged in successfully in {browser}?")
instaloader.context.username = username instaloader.context.username = username
print(f"{username} has been successfully logged in.") print(f"{username} has been successfully logged in.")
next_step_text = (f"Next: Run instaloader --login={username} as it is required to download high quality media " print(f"Next time use --login={username} to reuse the same session.")
"and to make full use of instaloader's features.")
print(textwrap.fill(next_step_text))
def _main(instaloader: Instaloader, targetlist: List[str], def _main(instaloader: Instaloader, targetlist: List[str],
@ -204,7 +201,7 @@ def _main(instaloader: Instaloader, targetlist: List[str],
instaloader.context.log("Logged in as %s." % username) instaloader.context.log("Logged in as %s." % username)
# since 4.2.9 login is required for geotags # since 4.2.9 login is required for geotags
if instaloader.download_geotags and not instaloader.context.is_logged_in: if instaloader.download_geotags and not instaloader.context.is_logged_in:
instaloader.context.error("Warning: Use --login to download geotags of posts.") instaloader.context.error("Warning: Login is required to download geotags of posts.")
# Try block for KeyboardInterrupt (save session on ^C) # Try block for KeyboardInterrupt (save session on ^C)
profiles = set() profiles = set()
anonymous_retry_profiles = set() anonymous_retry_profiles = set()
@ -299,7 +296,7 @@ def _main(instaloader: Instaloader, targetlist: List[str],
' '.join([p.username for p in profiles]))) ' '.join([p.username for p in profiles])))
if instaloader.context.iphone_support and profiles and (download_profile_pic or download_posts) and \ if instaloader.context.iphone_support and profiles and (download_profile_pic or download_posts) and \
not instaloader.context.is_logged_in: not instaloader.context.is_logged_in:
instaloader.context.log("Hint: Use --login to download higher-quality versions of pictures.") instaloader.context.log("Hint: Login to download higher-quality versions of pictures.")
instaloader.download_profiles(profiles, instaloader.download_profiles(profiles,
download_profile_pic, download_posts, download_tagged, download_igtv, download_profile_pic, download_posts, download_tagged, download_igtv,
download_highlights, download_stories, download_highlights, download_stories,
@ -346,17 +343,17 @@ def main():
help="Download profile. If an already-downloaded profile has been renamed, Instaloader " help="Download profile. If an already-downloaded profile has been renamed, Instaloader "
"automatically finds it by its unique ID and renames the folder likewise.") "automatically finds it by its unique ID and renames the folder likewise.")
g_targets.add_argument('_at_profile', nargs='*', metavar="@profile", g_targets.add_argument('_at_profile', nargs='*', metavar="@profile",
help="Download all followees of profile. Requires --login. " help="Download all followees of profile. Requires login. "
"Consider using :feed rather than @yourself.") "Consider using :feed rather than @yourself.")
g_targets.add_argument('_hashtag', nargs='*', metavar='"#hashtag"', help="Download #hashtag.") g_targets.add_argument('_hashtag', nargs='*', metavar='"#hashtag"', help="Download #hashtag.")
g_targets.add_argument('_location', nargs='*', metavar='%location_id', g_targets.add_argument('_location', nargs='*', metavar='%location_id',
help="Download %%location_id. Requires --login.") help="Download %%location_id. Requires login.")
g_targets.add_argument('_feed', nargs='*', metavar=":feed", g_targets.add_argument('_feed', nargs='*', metavar=":feed",
help="Download pictures from your feed. Requires --login.") help="Download pictures from your feed. Requires login.")
g_targets.add_argument('_stories', nargs='*', metavar=":stories", g_targets.add_argument('_stories', nargs='*', metavar=":stories",
help="Download the stories of your followees. Requires --login.") help="Download the stories of your followees. Requires login.")
g_targets.add_argument('_saved', nargs='*', metavar=":saved", g_targets.add_argument('_saved', nargs='*', metavar=":saved",
help="Download the posts that you marked as saved. Requires --login.") help="Download the posts that you marked as saved. Requires login.")
g_targets.add_argument('_singlepost', nargs='*', metavar="-- -shortcode", g_targets.add_argument('_singlepost', nargs='*', metavar="-- -shortcode",
help="Download the post with the given shortcode") help="Download the post with the given shortcode")
g_targets.add_argument('_json', nargs='*', metavar="filename.json[.xz]", g_targets.add_argument('_json', nargs='*', metavar="filename.json[.xz]",
@ -387,11 +384,11 @@ def main():
help='Download geotags when available. Geotags are stored as a ' help='Download geotags when available. Geotags are stored as a '
'text file with the location\'s name and a Google Maps link. ' 'text file with the location\'s name and a Google Maps link. '
'This requires an additional request to the Instagram ' 'This requires an additional request to the Instagram '
'server for each picture. Requires --login.') 'server for each picture. Requires login.')
g_post.add_argument('-C', '--comments', action='store_true', g_post.add_argument('-C', '--comments', action='store_true',
help='Download and update comments for each post. ' help='Download and update comments for each post. '
'This requires an additional request to the Instagram ' 'This requires an additional request to the Instagram '
'server for each post, which is why it is disabled by default.') 'server for each post, which is why it is disabled by default. Requires login.')
g_post.add_argument('--no-captions', action='store_true', g_post.add_argument('--no-captions', action='store_true',
help='Do not create txt files.') help='Do not create txt files.')
g_post.add_argument('--post-metadata-txt', action='append', g_post.add_argument('--post-metadata-txt', action='append',
@ -405,11 +402,11 @@ def main():
g_post.add_argument('--no-compress-json', action='store_true', g_post.add_argument('--no-compress-json', action='store_true',
help='Do not xz compress JSON files, rather create pretty formatted JSONs.') help='Do not xz compress JSON files, rather create pretty formatted JSONs.')
g_prof.add_argument('-s', '--stories', action='store_true', g_prof.add_argument('-s', '--stories', action='store_true',
help='Also download stories of each profile that is downloaded. Requires --login.') help='Also download stories of each profile that is downloaded. Requires login.')
g_prof.add_argument('--stories-only', action='store_true', g_prof.add_argument('--stories-only', action='store_true',
help=SUPPRESS) help=SUPPRESS)
g_prof.add_argument('--highlights', action='store_true', g_prof.add_argument('--highlights', action='store_true',
help='Also download highlights of each profile that is downloaded. Requires --login.') help='Also download highlights of each profile that is downloaded. Requires login.')
g_prof.add_argument('--tagged', action='store_true', g_prof.add_argument('--tagged', action='store_true',
help='Also download posts where each profile is tagged.') help='Also download posts where each profile is tagged.')
g_prof.add_argument('--igtv', action='store_true', g_prof.add_argument('--igtv', action='store_true',
@ -441,7 +438,8 @@ def main():
'Instaloader can login to Instagram. This allows downloading private profiles. ' 'Instaloader can login to Instagram. This allows downloading private profiles. '
'To login, pass the --login option. Your session cookie (not your password!) ' 'To login, pass the --login option. Your session cookie (not your password!) '
'will be saved to a local file to be reused next time you want Instaloader ' 'will be saved to a local file to be reused next time you want Instaloader '
'to login.') 'to login. Instead of --login, the --load-cookies option can be used to '
'import a session from a browser.')
g_login.add_argument('-l', '--login', metavar='YOUR-USERNAME', g_login.add_argument('-l', '--login', metavar='YOUR-USERNAME',
help='Login name (profile name) for your Instagram account.') help='Login name (profile name) for your Instagram account.')
g_login.add_argument('-b', '--load-cookies', metavar='BROWSER-NAME', g_login.add_argument('-b', '--load-cookies', metavar='BROWSER-NAME',
@ -508,8 +506,8 @@ def main():
args = parser.parse_args() args = parser.parse_args()
try: try:
if args.login is None and (args.stories or args.stories_only): if (args.login is None and args.load_cookies is None) and (args.stories or args.stories_only):
print("--login=USERNAME required to download stories.", file=sys.stderr) print("Login is required to download stories.", file=sys.stderr)
args.stories = False args.stories = False
if args.stories_only: if args.stories_only:
raise InvalidArgumentException() raise InvalidArgumentException()

View File

@ -77,7 +77,7 @@ def _requires_login(func: Callable) -> Callable:
@wraps(func) @wraps(func)
def call(instaloader, *args, **kwargs): def call(instaloader, *args, **kwargs):
if not instaloader.context.is_logged_in: if not instaloader.context.is_logged_in:
raise LoginRequiredException("--login=USERNAME required.") raise LoginRequiredException("Login required.")
return func(instaloader, *args, **kwargs) return func(instaloader, *args, **kwargs)
return call return call
@ -1465,7 +1465,7 @@ class Instaloader:
if tagged or igtv or highlights or posts: if tagged or igtv or highlights or posts:
if (not self.context.is_logged_in and if (not self.context.is_logged_in and
profile.is_private): profile.is_private):
raise LoginRequiredException("--login=USERNAME required.") raise LoginRequiredException("Login required.")
if (self.context.username != profile.username and if (self.context.username != profile.username and
profile.is_private and profile.is_private and
not profile.followed_by_viewer): not profile.followed_by_viewer):

View File

@ -305,9 +305,10 @@ class InstaloaderContext:
resp_json['two_factor_info']['two_factor_identifier']) resp_json['two_factor_info']['two_factor_identifier'])
raise TwoFactorAuthRequiredException("Login error: two-factor authentication required.") raise TwoFactorAuthRequiredException("Login error: two-factor authentication required.")
if resp_json.get('checkpoint_url'): if resp_json.get('checkpoint_url'):
raise LoginException("Login: Checkpoint required. Point your browser to " raise LoginException(
"https://www.instagram.com{} - " f"Login: Checkpoint required. Point your browser to {resp_json.get('checkpoint_url')} - "
"follow the instructions, then retry.".format(resp_json.get('checkpoint_url'))) f"follow the instructions, then retry."
)
if resp_json['status'] != 'ok': if resp_json['status'] != 'ok':
if 'message' in resp_json: if 'message' in resp_json:
raise LoginException("Login error: \"{}\" status, message \"{}\".".format(resp_json['status'], raise LoginException("Login error: \"{}\" status, message \"{}\".".format(resp_json['status'],
@ -425,7 +426,7 @@ class InstaloaderContext:
if (redirect_url.startswith('https://www.instagram.com/accounts/login') or if (redirect_url.startswith('https://www.instagram.com/accounts/login') or
redirect_url.startswith('https://i.instagram.com/accounts/login')): redirect_url.startswith('https://i.instagram.com/accounts/login')):
if not self.is_logged_in: if not self.is_logged_in:
raise LoginRequiredException("Redirected to login page. Use --login.") raise LoginRequiredException("Redirected to login page. Use --login or --load-cookies.")
raise AbortDownloadException("Redirected to login page. You've been logged out, please wait " + raise AbortDownloadException("Redirected to login page. You've been logged out, please wait " +
"some time, recreate the session and try again") "some time, recreate the session and try again")
if redirect_url.startswith('https://{}/'.format(host)): if redirect_url.startswith('https://{}/'.format(host)):

View File

@ -330,7 +330,7 @@ class Post:
if not self._context.iphone_support: if not self._context.iphone_support:
raise IPhoneSupportDisabledException("iPhone support is disabled.") raise IPhoneSupportDisabledException("iPhone support is disabled.")
if not self._context.is_logged_in: if not self._context.is_logged_in:
raise LoginRequiredException("--login required to access iPhone media info endpoint.") raise LoginRequiredException("Login required to access iPhone media info endpoint.")
if not self._iphone_struct_: if not self._iphone_struct_:
data = self._context.get_iphone_json(path='api/v1/media/{}/info/'.format(self.mediaid), params={}) data = self._context.get_iphone_json(path='api/v1/media/{}/info/'.format(self.mediaid), params={})
self._iphone_struct_ = data['items'][0] self._iphone_struct_ = data['items'][0]
@ -685,6 +685,9 @@ class Post:
.. versionchanged:: 4.7 .. versionchanged:: 4.7
Change return type to ``Iterable``. Change return type to ``Iterable``.
""" """
if not self._context.is_logged_in:
raise LoginRequiredException("Login required to access comments of a post.")
def _postcommentanswer(node): def _postcommentanswer(node):
return PostCommentAnswer(id=int(node['id']), return PostCommentAnswer(id=int(node['id']),
created_at_utc=datetime.utcfromtimestamp(node['created_at']), created_at_utc=datetime.utcfromtimestamp(node['created_at']),
@ -749,7 +752,7 @@ class Post:
Require being logged in (as required by Instagram). Require being logged in (as required by Instagram).
""" """
if not self._context.is_logged_in: if not self._context.is_logged_in:
raise LoginRequiredException("--login required to access likes of a post.") raise LoginRequiredException("Login required to access likes of a post.")
if self.likes == 0: if self.likes == 0:
# Avoid doing additional requests if there are no comments # Avoid doing additional requests if there are no comments
return return
@ -926,7 +929,7 @@ class Profile:
.. versionadded:: 4.5.2""" .. versionadded:: 4.5.2"""
if not context.is_logged_in: if not context.is_logged_in:
raise LoginRequiredException("--login required to access own profile.") raise LoginRequiredException("Login required to access own profile.")
return cls(context, context.graphql_query("d6f4427fbe92d846298cf93df0b937d3", {})["data"]["user"]) return cls(context, context.graphql_query("d6f4427fbe92d846298cf93df0b937d3", {})["data"]["user"])
def _asdict(self): def _asdict(self):
@ -980,7 +983,7 @@ class Profile:
if not self._context.iphone_support: if not self._context.iphone_support:
raise IPhoneSupportDisabledException("iPhone support is disabled.") raise IPhoneSupportDisabledException("iPhone support is disabled.")
if not self._context.is_logged_in: if not self._context.is_logged_in:
raise LoginRequiredException("--login required to access iPhone profile info endpoint.") raise LoginRequiredException("Login required to access iPhone profile info endpoint.")
if not self._iphone_struct_: if not self._iphone_struct_:
data = self._context.get_iphone_json(path='api/v1/users/{}/info/'.format(self.userid), params={}) data = self._context.get_iphone_json(path='api/v1/users/{}/info/'.format(self.userid), params={})
self._iphone_struct_ = data['user'] self._iphone_struct_ = data['user']
@ -1187,7 +1190,7 @@ class Profile:
:rtype: NodeIterator[Post]""" :rtype: NodeIterator[Post]"""
if self.username != self._context.username: if self.username != self._context.username:
raise LoginRequiredException("--login={} required to get that profile's saved posts.".format(self.username)) raise LoginRequiredException(f"Login as {self.username} required to get that profile's saved posts.")
return NodeIterator( return NodeIterator(
self._context, self._context,
@ -1247,7 +1250,7 @@ class Profile:
.. versionadded:: 4.10 .. versionadded:: 4.10
""" """
if not self._context.is_logged_in: if not self._context.is_logged_in:
raise LoginRequiredException("--login required to get a profile's followers.") raise LoginRequiredException("Login required to get a profile's followers.")
self._obtain_metadata() self._obtain_metadata()
return NodeIterator( return NodeIterator(
self._context, self._context,
@ -1266,7 +1269,7 @@ class Profile:
:rtype: NodeIterator[Profile] :rtype: NodeIterator[Profile]
""" """
if not self._context.is_logged_in: if not self._context.is_logged_in:
raise LoginRequiredException("--login required to get a profile's followers.") raise LoginRequiredException("Login required to get a profile's followers.")
self._obtain_metadata() self._obtain_metadata()
return NodeIterator( return NodeIterator(
self._context, self._context,
@ -1285,7 +1288,7 @@ class Profile:
:rtype: NodeIterator[Profile] :rtype: NodeIterator[Profile]
""" """
if not self._context.is_logged_in: if not self._context.is_logged_in:
raise LoginRequiredException("--login required to get a profile's followees.") raise LoginRequiredException("Login required to get a profile's followees.")
self._obtain_metadata() self._obtain_metadata()
return NodeIterator( return NodeIterator(
self._context, self._context,
@ -1304,7 +1307,7 @@ class Profile:
.. versionadded:: 4.4 .. versionadded:: 4.4
""" """
if not self._context.is_logged_in: if not self._context.is_logged_in:
raise LoginRequiredException("--login required to get a profile's similar accounts.") raise LoginRequiredException("Login required to get a profile's similar accounts.")
self._obtain_metadata() self._obtain_metadata()
yield from (Profile(self._context, edge["node"]) for edge in yield from (Profile(self._context, edge["node"]) for edge in
self._context.graphql_query("ad99dd9d3646cc3c0dda65debcd266a7", self._context.graphql_query("ad99dd9d3646cc3c0dda65debcd266a7",
@ -1383,7 +1386,7 @@ class StoryItem:
if not self._context.iphone_support: if not self._context.iphone_support:
raise IPhoneSupportDisabledException("iPhone support is disabled.") raise IPhoneSupportDisabledException("iPhone support is disabled.")
if not self._context.is_logged_in: if not self._context.is_logged_in:
raise LoginRequiredException("--login required to access iPhone media info endpoint.") raise LoginRequiredException("Login required to access iPhone media info endpoint.")
if not self._iphone_struct_: if not self._iphone_struct_:
data = self._context.get_iphone_json( data = self._context.get_iphone_json(
path='api/v1/feed/reels_media/?reel_ids={}'.format(self.owner_id), params={} path='api/v1/feed/reels_media/?reel_ids={}'.format(self.owner_id), params={}