mirror of
https://github.com/instaloader/instaloader.git
synced 2026-10-16 13:48:03 -07:00
Merge branch 'upcoming/v4.15'
This commit is contained in:
@@ -14,11 +14,11 @@ jobs:
|
|||||||
- name: "Setup Python"
|
- name: "Setup Python"
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: 3.12
|
python-version: 3.14
|
||||||
cache: 'pipenv'
|
cache: 'pipenv'
|
||||||
- name: "Install Dependencies"
|
- name: "Install Dependencies"
|
||||||
run: |
|
run: |
|
||||||
python -m pip install pipenv==2024.2.0
|
python -m pip install pipenv==2025.0.4
|
||||||
pipenv --python `python --version | grep -Eo '3\.[0-9]+'` sync --dev
|
pipenv --python `python --version | grep -Eo '3\.[0-9]+'` sync --dev
|
||||||
- name: "Build Documentation"
|
- name: "Build Documentation"
|
||||||
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"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Instaloader Repository
|
- name: Checkout Instaloader Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -20,9 +20,10 @@ jobs:
|
|||||||
cache: 'pipenv'
|
cache: 'pipenv'
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install pipenv==2024.2.0
|
python -m pip install pipenv==2025.0.4
|
||||||
pipenv --python `python --version | grep -Eo '3\.[0-9]+'` sync --dev
|
pipenv --python `python --version | grep -Eo '3\.[0-9]+'` sync --dev
|
||||||
- name: PyLint
|
- name: PyLint
|
||||||
|
if: matrix.python-version != '3.9'
|
||||||
run: pipenv --python `python --version | grep -Eo '3\.[0-9]+'` run pylint instaloader
|
run: pipenv --python `python --version | grep -Eo '3\.[0-9]+'` run pylint instaloader
|
||||||
- name: MyPy
|
- name: MyPy
|
||||||
run: pipenv --python `python --version | grep -Eo '3\.[0-9]+'` run mypy -m instaloader
|
run: pipenv --python `python --version | grep -Eo '3\.[0-9]+'` run mypy -m instaloader
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ jobs:
|
|||||||
- name: "Setup Python"
|
- name: "Setup Python"
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: 3.12
|
python-version: 3.14
|
||||||
- name: "Get the tagged version"
|
- name: "Get the tagged version"
|
||||||
id: get_version
|
id: get_version
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ jobs:
|
|||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "3.12"
|
python-version: "3.14"
|
||||||
cache: 'pipenv'
|
cache: 'pipenv'
|
||||||
architecture: x64
|
architecture: x64
|
||||||
- name: Get the tagged version
|
- name: Get the tagged version
|
||||||
|
|||||||
@@ -433,6 +433,7 @@ disable=bad-inline-option,
|
|||||||
too-many-instance-attributes,
|
too-many-instance-attributes,
|
||||||
too-many-lines,
|
too-many-lines,
|
||||||
too-many-locals,
|
too-many-locals,
|
||||||
|
too-many-positional-arguments,
|
||||||
too-many-public-methods,
|
too-many-public-methods,
|
||||||
too-many-statements,
|
too-many-statements,
|
||||||
unnecessary-lambda-assignment,
|
unnecessary-lambda-assignment,
|
||||||
|
|||||||
Generated
+739
-564
File diff suppressed because it is too large
Load Diff
@@ -44,7 +44,7 @@ with open('__main__.py', 'w+') as f:
|
|||||||
f.writelines(lines)
|
f.writelines(lines)
|
||||||
|
|
||||||
# install dependencies and invoke PyInstaller
|
# install dependencies and invoke PyInstaller
|
||||||
commands = ["pip install pipenv==2024.2.0",
|
commands = ["pip install pipenv==2025.0.4",
|
||||||
f"pipenv --python {sys.version_info.major}.{sys.version_info.minor} sync --dev",
|
f"pipenv --python {sys.version_info.major}.{sys.version_info.minor} sync --dev",
|
||||||
"pipenv run pyinstaller --log-level=DEBUG instaloader.spec"]
|
"pipenv run pyinstaller --log-level=DEBUG instaloader.spec"]
|
||||||
|
|
||||||
|
|||||||
+12
-10
@@ -208,22 +208,24 @@ Instead of :option:`--login`, it is possible to use
|
|||||||
|
|
||||||
.. option:: --load-cookies BROWSER-NAME, -b BROWSER-NAME
|
.. option:: --load-cookies BROWSER-NAME, -b BROWSER-NAME
|
||||||
|
|
||||||
Use Instagram cookie in your browser to login.
|
Load Instagram session cookies from a browser instead of using Instaloader's own login mechanism.
|
||||||
This feature requires the browser_cookie3 library.
|
This method requires the `browser_cookie3 <https://github.com/borisbabic/browser_cookie3>`__ library.
|
||||||
Compatible with :option:`--cookiefile` if you want to load cookies from browser profiles.
|
By default, cookies are loaded from the browser's main profile, but you can specify a custom cookie file using :option:`--cookiefile`.
|
||||||
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.
|
|
||||||
|
|
||||||
In subsequent runs, you can just use :option:`--login` to reuse the
|
Since :option:`--load-cookies` and :option:`--login` are alternative ways to acquire a session, they cannot be used together when initially acquiring a session.
|
||||||
same session, which is saved by Instaloader.
|
|
||||||
|
Supported browsers: Arc, Brave, Chrome, Chromium, Edge, Firefox, LibreWolf, Opera, Opera_GX, Safari, and Vivaldi.
|
||||||
|
|
||||||
|
Once logged in via browser cookies, Instaloader saves the session, allowing you to use :option:`--login` in subsequent runs.
|
||||||
|
|
||||||
.. versionadded:: 4.11
|
.. versionadded:: 4.11
|
||||||
|
|
||||||
.. option:: --cookiefile COOKIE-FILE, -B COOKIE-FILE
|
.. option:: --cookiefile COOKIE-FILE, -B COOKIE-FILE
|
||||||
|
|
||||||
Cookie file path of a browser profile to load cookies from.
|
Specifies a cookie file path for :option:`--load-cookies` to load cookies from a particular browser profile.
|
||||||
|
This option must be used together with :option:`--load-cookies` and does not work on its own.
|
||||||
|
|
||||||
.. versionadded:: 4.11
|
.. versionadded:: 4.11
|
||||||
|
|
||||||
.. option:: --sessionfile SESSIONFILE, -f SESSIONFILE
|
.. option:: --sessionfile SESSIONFILE, -f SESSIONFILE
|
||||||
|
|
||||||
@@ -310,7 +312,7 @@ How to Download
|
|||||||
.. option:: --user-agent USER_AGENT
|
.. option:: --user-agent USER_AGENT
|
||||||
|
|
||||||
User Agent to use for HTTP requests. Per default, Instaloader pretends being
|
User Agent to use for HTTP requests. Per default, Instaloader pretends being
|
||||||
Chrome/127 on Linux.
|
Chrome/142 on Linux.
|
||||||
|
|
||||||
.. option:: --max-connection-attempts N
|
.. option:: --max-connection-attempts N
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import instaloader
|
|||||||
|
|
||||||
L = instaloader.Instaloader()
|
L = instaloader.Instaloader()
|
||||||
|
|
||||||
posts = instaloader.Hashtag.from_name(L.context, 'urbanphotography').get_posts()
|
posts = instaloader.Hashtag.from_name(L.context, 'urbanphotography').get_posts_resumable()
|
||||||
|
|
||||||
users = set()
|
users = set()
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import instaloader
|
|||||||
|
|
||||||
L = instaloader.Instaloader()
|
L = instaloader.Instaloader()
|
||||||
|
|
||||||
posts = instaloader.Hashtag.from_name(L.context, "urbanphotography").get_posts()
|
posts = instaloader.Hashtag.from_name(L.context, "urbanphotography").get_posts_resumable()
|
||||||
|
|
||||||
SINCE = datetime(2020, 5, 10) # further from today, inclusive
|
SINCE = datetime(2020, 5, 10) # further from today, inclusive
|
||||||
UNTIL = datetime(2020, 5, 11) # closer to today, not inclusive
|
UNTIL = datetime(2020, 5, 11) # closer to today, not inclusive
|
||||||
|
|||||||
@@ -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.14.2'
|
__version__ = '4.15a1'
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -737,19 +737,20 @@ class Instaloader:
|
|||||||
suffix: Optional[str] = str(edge_number)
|
suffix: Optional[str] = str(edge_number)
|
||||||
if '{filename}' in self.filename_pattern:
|
if '{filename}' in self.filename_pattern:
|
||||||
suffix = None
|
suffix = None
|
||||||
if self.download_pictures and (not sidecar_node.is_video or self.download_video_thumbnails):
|
video_url = sidecar_node.video_url
|
||||||
|
if self.download_pictures and (video_url is None or self.download_video_thumbnails):
|
||||||
# pylint:disable=cell-var-from-loop
|
# pylint:disable=cell-var-from-loop
|
||||||
sidecar_filename = self.__prepare_filename(filename_template,
|
sidecar_filename = self.__prepare_filename(filename_template,
|
||||||
lambda: sidecar_node.display_url)
|
lambda: sidecar_node.display_url)
|
||||||
# Download sidecar picture or video thumbnail (--no-pictures implies --no-video-thumbnails)
|
# Download sidecar picture or video thumbnail (--no-pictures implies --no-video-thumbnails)
|
||||||
downloaded &= self.download_pic(filename=sidecar_filename, url=sidecar_node.display_url,
|
downloaded &= self.download_pic(filename=sidecar_filename, url=sidecar_node.display_url,
|
||||||
mtime=post.date_local, filename_suffix=suffix)
|
mtime=post.date_local, filename_suffix=suffix)
|
||||||
if sidecar_node.is_video and self.download_videos:
|
if video_url is not None and self.download_videos:
|
||||||
# pylint:disable=cell-var-from-loop
|
# pylint:disable=cell-var-from-loop
|
||||||
sidecar_filename = self.__prepare_filename(filename_template,
|
sidecar_filename = self.__prepare_filename(filename_template,
|
||||||
lambda: sidecar_node.video_url)
|
lambda: video_url)
|
||||||
# Download sidecar video if desired
|
# Download sidecar video if desired
|
||||||
downloaded &= self.download_pic(filename=sidecar_filename, url=sidecar_node.video_url,
|
downloaded &= self.download_pic(filename=sidecar_filename, url=video_url,
|
||||||
mtime=post.date_local, filename_suffix=suffix)
|
mtime=post.date_local, filename_suffix=suffix)
|
||||||
else:
|
else:
|
||||||
downloaded = False
|
downloaded = False
|
||||||
|
|||||||
@@ -32,15 +32,15 @@ def copy_session(session: requests.Session, request_timeout: Optional[float] = N
|
|||||||
|
|
||||||
def default_user_agent() -> str:
|
def default_user_agent() -> str:
|
||||||
return ('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 '
|
return ('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 '
|
||||||
'(KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36')
|
'(KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36')
|
||||||
|
|
||||||
|
|
||||||
def default_iphone_headers() -> Dict[str, Any]:
|
def default_iphone_headers() -> Dict[str, Any]:
|
||||||
return {'User-Agent': 'Instagram 273.0.0.16.70 (iPad13,8; iOS 16_3; en_US; en-US; ' \
|
return {'User-Agent': 'Instagram 361.0.0.35.82 (iPad13,8; iOS 18_0; en_US; en-US; ' \
|
||||||
'scale=2.00; 2048x2732; 452417278) AppleWebKit/420+',
|
'scale=2.00; 2048x2732; 674117118) AppleWebKit/420+',
|
||||||
'x-ads-opt-out': '1',
|
'x-ads-opt-out': '1',
|
||||||
'x-bloks-is-panorama-enabled': 'true',
|
'x-bloks-is-panorama-enabled': 'true',
|
||||||
'x-bloks-version-id': '01507c21540f73e2216b6f62a11a5b5e51aa85491b72475c080da35b1228ddd6',
|
'x-bloks-version-id': '16b7bd25c6c06886d57c4d455265669345a2d96625385b8ee30026ac2dc5ed97',
|
||||||
'x-fb-client-ip': 'True',
|
'x-fb-client-ip': 'True',
|
||||||
'x-fb-connection-type': 'wifi',
|
'x-fb-connection-type': 'wifi',
|
||||||
'x-fb-http-engine': 'Liger',
|
'x-fb-http-engine': 'Liger',
|
||||||
|
|||||||
+27
-12
@@ -20,7 +20,7 @@ class PostSidecarNode(NamedTuple):
|
|||||||
"""Item of a Sidecar Post."""
|
"""Item of a Sidecar Post."""
|
||||||
is_video: bool
|
is_video: bool
|
||||||
display_url: str
|
display_url: str
|
||||||
video_url: str
|
video_url: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
PostSidecarNode.is_video.__doc__ = "Whether this node is a video."
|
PostSidecarNode.is_video.__doc__ = "Whether this node is a video."
|
||||||
@@ -1206,18 +1206,33 @@ class Profile:
|
|||||||
|
|
||||||
:rtype: NodeIterator[Post]"""
|
:rtype: NodeIterator[Post]"""
|
||||||
self._obtain_metadata()
|
self._obtain_metadata()
|
||||||
|
logged_in = self._context.is_logged_in
|
||||||
return NodeIterator(
|
return NodeIterator(
|
||||||
context = self._context,
|
context=self._context,
|
||||||
edge_extractor = lambda d: d['data']['xdt_api__v1__feed__user_timeline_graphql_connection'],
|
edge_extractor=(
|
||||||
node_wrapper = lambda n: Post.from_iphone_struct(self._context, n),
|
(lambda d: d["data"]["xdt_api__v1__feed__user_timeline_graphql_connection"])
|
||||||
query_variables = {'data': {
|
if logged_in
|
||||||
'count': 12, 'include_relationship_info': True,
|
else (lambda d: d["data"]["user"]["edge_owner_to_timeline_media"])
|
||||||
'latest_besties_reel_media': True, 'latest_reel_media': True},
|
),
|
||||||
'username': self.username},
|
node_wrapper=(
|
||||||
query_referer = 'https://www.instagram.com/{0}/'.format(self.username),
|
(lambda n: Post.from_iphone_struct(self._context, n))
|
||||||
is_first = Profile._make_is_newest_checker(),
|
if logged_in
|
||||||
doc_id = '7898261790222653',
|
else (lambda n: Post(self._context, n, self))
|
||||||
query_hash = None,
|
),
|
||||||
|
query_variables={
|
||||||
|
"data": {
|
||||||
|
"count": 12,
|
||||||
|
"include_relationship_info": True,
|
||||||
|
"latest_besties_reel_media": True,
|
||||||
|
"latest_reel_media": True,
|
||||||
|
},
|
||||||
|
**({"username": self.username} if logged_in else {"id": self.userid}),
|
||||||
|
},
|
||||||
|
query_referer="https://www.instagram.com/{0}/".format(self.username),
|
||||||
|
is_first=Profile._make_is_newest_checker(),
|
||||||
|
doc_id="7898261790222653" if logged_in else "7950326061742207",
|
||||||
|
query_hash=None,
|
||||||
|
first_data=(None if logged_in else self._metadata("edge_owner_to_timeline_media")),
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_saved_posts(self) -> NodeIterator[Post]:
|
def get_saved_posts(self) -> NodeIterator[Post]:
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ setup(
|
|||||||
'Programming Language :: Python :: 3.11',
|
'Programming Language :: Python :: 3.11',
|
||||||
'Programming Language :: Python :: 3.12',
|
'Programming Language :: Python :: 3.12',
|
||||||
'Programming Language :: Python :: 3.13',
|
'Programming Language :: Python :: 3.13',
|
||||||
|
'Programming Language :: Python :: 3.14',
|
||||||
'Programming Language :: Python :: 3 :: Only',
|
'Programming Language :: Python :: 3 :: Only',
|
||||||
'Topic :: Internet',
|
'Topic :: Internet',
|
||||||
'Topic :: Multimedia :: Graphics'
|
'Topic :: Multimedia :: Graphics'
|
||||||
|
|||||||
Reference in New Issue
Block a user